opal-security 2.0.20 → 2.1.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,46 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const command_1 = require("@oclif/command");
4
- const handler_1 = require("../../handler");
5
4
  const apollo_1 = require("../../lib/apollo");
6
5
  const cmd_1 = require("../../lib/cmd");
7
6
  const ssh_1 = require("../../lib/ssh");
8
- const common_1 = require("../../lib/common");
9
- const StartSSHSessionDocument = `
10
- mutation StartSSHSession($id: ResourceId!, $accessLevel: ResourceAccessLevelInput!, $sessionId: SessionId) {
11
- createSession(input: {resourceId: $id, accessLevel: $accessLevel, sessionId: $sessionId}) {
12
- __typename
13
- ... on CreateSessionResult {
14
- session {
15
- id
16
- endTime
17
- metadata {
18
- ... on AwsIamFederatedSSMSession {
19
- awsAccessKeyId
20
- awsSecretAccessKey
21
- awsSessionToken
22
- awsLoginUrl
23
- federatedArn
24
- ec2InstanceId
25
- ec2Region
26
- }
27
- }
28
- }
29
- }
30
- ... on SessionNotFoundError {
31
- message
32
- }
33
- ... on MfaInvalidError {
34
- message
35
- }
36
- ... on ResourceNotFoundError {
37
- message
38
- }
39
- ... on EndSystemAuthorizationError {
40
- message
41
- }
42
- }
43
- }`;
7
+ const resources_1 = require("../../lib/resources");
8
+ const sessions_1 = require("../../lib/sessions");
9
+ const start_1 = require("./start");
44
10
  class StartSCPSession extends command_1.Command {
45
11
  async run() {
46
12
  cmd_1.setMostRecentCommand(this);
@@ -60,38 +26,23 @@ class StartSCPSession extends command_1.Command {
60
26
  instanceId = selectedInstance.id;
61
27
  instanceName = selectedInstance.name;
62
28
  }
63
- const { resp, error } = await handler_1.runMutation({
64
- command: this,
65
- query: StartSSHSessionDocument,
66
- variables: { id: instanceId, accessLevel: cmd_1.DEFAULT_ACCESS_LEVEL, sessionId },
67
- });
68
- switch (resp === null || resp === void 0 ? void 0 : resp.data.createSession.__typename) {
69
- case 'CreateSessionResult': {
70
- const metadata = resp.data.createSession.session.metadata;
71
- switch (metadata === null || metadata === void 0 ? void 0 : metadata.__typename) {
72
- case 'AwsIamFederatedSSMSession': {
73
- const envVars = {
74
- AWS_ACCESS_KEY_ID: metadata.awsAccessKeyId,
75
- AWS_SECRET_ACCESS_KEY: metadata.awsSecretAccessKey,
76
- AWS_SESSION_TOKEN: metadata.awsSessionToken,
77
- };
78
- // Run SCP script
79
- const scpCmd = `$SCRIPT_PATH/../../scripts/ssh_ssm_scp_to_server.sh ${metadata.ec2InstanceId} ${flags.user} ${flags.src} ${metadata.ec2Region} ${flags.dest}`;
80
- const outputMessage = `from "${flags.src}" locally to "${flags.dest}" on ${instanceName ? `"${instanceName}" instance` : 'instance'}.`;
81
- cmd_1.runCommandSpawn(scpCmd, `Copied ${outputMessage}`, `Failed to copy ${outputMessage}.`, envVars);
82
- break;
83
- }
84
- default:
85
- apollo_1.printRequestOutput(this, resp, error);
86
- }
87
- break;
88
- }
89
- case 'MfaInvalidError': {
90
- common_1.handleMfaRedirect(this, instanceId);
29
+ const session = await sessions_1.getOrCreateSession(this, instanceId, resources_1.DEFAULT_ACCESS_LEVEL, sessionId, start_1.Ec2SessionMetadataFragment);
30
+ const metadata = session.metadata;
31
+ switch (metadata === null || metadata === void 0 ? void 0 : metadata.__typename) {
32
+ case 'AwsIamFederatedSSMSession': {
33
+ const envVars = {
34
+ AWS_ACCESS_KEY_ID: metadata.awsAccessKeyId,
35
+ AWS_SECRET_ACCESS_KEY: metadata.awsSecretAccessKey,
36
+ AWS_SESSION_TOKEN: metadata.awsSessionToken,
37
+ };
38
+ // Run SCP script
39
+ const scpCmd = `$SCRIPT_PATH/../../scripts/ssh_ssm_scp_to_server.sh ${metadata.ec2InstanceId} ${flags.user} ${flags.src} ${metadata.ec2Region} ${flags.dest}`;
40
+ const outputMessage = `from "${flags.src}" locally to "${flags.dest}" on ${instanceName ? `"${instanceName}" instance` : 'instance'}.`;
41
+ cmd_1.runCommandSpawn(scpCmd, `Copied ${outputMessage}`, `Failed to copy ${outputMessage}.`, envVars);
91
42
  break;
92
43
  }
93
44
  default:
94
- apollo_1.printRequestOutput(this, resp, error);
45
+ return apollo_1.handleError(this, undefined, session);
95
46
  }
96
47
  }
97
48
  }
@@ -118,7 +69,7 @@ StartSCPSession.flags = {
118
69
  multiple: false,
119
70
  required: false,
120
71
  default: 'ssm-user',
121
- description: "Pick which user you want to run SCP over. Keep in mind not all users will have access to each other's home directory.",
72
+ description: 'Pick which user you want to run SCP over. Keep in mind not all users will have access to each other\'s home directory.',
122
73
  }),
123
74
  id: command_1.flags.string({
124
75
  multiple: false,
@@ -1,4 +1,5 @@
1
1
  import { Command, flags } from '@oclif/command';
2
+ export declare const Ec2SessionMetadataFragment = "\n... on AwsIamFederatedSSMSession {\n awsAccessKeyId\n awsSecretAccessKey\n awsSessionToken\n awsLoginUrl\n federatedArn\n ec2InstanceId\n ec2Region\n}";
2
3
  export default class StartSSHSession extends Command {
3
4
  static description: string;
4
5
  static examples: string[];
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Ec2SessionMetadataFragment = void 0;
3
4
  const command_1 = require("@oclif/command");
4
5
  const handler_1 = require("../../handler");
5
6
  const cmd_1 = require("../../lib/cmd");
@@ -7,44 +8,17 @@ const apollo_1 = require("../../lib/apollo");
7
8
  const ssh_1 = require("../../lib/ssh");
8
9
  const get_1 = require("../../commands/resources/get");
9
10
  const aws_1 = require("../../lib/aws");
10
- const common_1 = require("../../lib/common");
11
- const StartSSHSessionDocument = `
12
- mutation StartSSHSession($id: ResourceId!, $accessLevel: ResourceAccessLevelInput!, $sessionId: SessionId) {
13
- createSession(input: {resourceId: $id, accessLevel: $accessLevel, sessionId: $sessionId}) {
14
- __typename
15
- ... on CreateSessionResult {
16
- session {
17
- id
18
- endTime
19
- metadata {
20
- ... on AwsIamFederatedSSMSession {
21
- awsAccessKeyId
22
- awsSecretAccessKey
23
- awsSessionToken
24
- awsLoginUrl
25
- federatedArn
26
- ec2InstanceId
27
- ec2Region
28
- }
29
- }
30
- }
31
- }
32
- ... on SessionNotFoundError {
33
- message
34
- }
35
- ... on MfaInvalidError {
36
- message
37
- }
38
- ... on OidcIDTokenNotFoundError {
39
- message
40
- }
41
- ... on ResourceNotFoundError {
42
- message
43
- }
44
- ... on EndSystemAuthorizationError {
45
- message
46
- }
47
- }
11
+ const resources_1 = require("../../lib/resources");
12
+ const sessions_1 = require("../../lib/sessions");
13
+ exports.Ec2SessionMetadataFragment = `
14
+ ... on AwsIamFederatedSSMSession {
15
+ awsAccessKeyId
16
+ awsSecretAccessKey
17
+ awsSessionToken
18
+ awsLoginUrl
19
+ federatedArn
20
+ ec2InstanceId
21
+ ec2Region
48
22
  }`;
49
23
  class StartSSHSession extends command_1.Command {
50
24
  async run() {
@@ -57,7 +31,6 @@ class StartSSHSession extends command_1.Command {
57
31
  let instanceId = flags.id;
58
32
  let instanceName = null;
59
33
  const sessionId = flags.sessionId;
60
- // TODO: RESOURCES-1: How do we grant access to a perm using ID
61
34
  if (!instanceId) {
62
35
  const selectedInstance = await ssh_1.selectComputeInstance(this, 'SSH into');
63
36
  if (!selectedInstance) {
@@ -67,63 +40,43 @@ class StartSSHSession extends command_1.Command {
67
40
  instanceName = selectedInstance.name;
68
41
  }
69
42
  else {
70
- const { resp: sshInstanceResp, error } = await handler_1.runQuery({
43
+ const { resp, error } = await handler_1.runQuery({
71
44
  command: this,
72
45
  query: get_1.GetResourceDocument,
73
46
  variables: {
74
47
  id: instanceId,
75
48
  },
76
49
  });
77
- if (error || !(sshInstanceResp === null || sshInstanceResp === void 0 ? void 0 : sshInstanceResp.data.resource.resource)) {
78
- apollo_1.printRequestOutput(this, sshInstanceResp, error);
79
- return;
50
+ if (error) {
51
+ return apollo_1.handleError(this, error, resp);
52
+ }
53
+ if (!(resp === null || resp === void 0 ? void 0 : resp.data.resource.resource)) {
54
+ return apollo_1.handleError(this, `Resource not found for ID: ${instanceId}`);
80
55
  }
81
56
  instanceName =
82
- (sshInstanceResp === null || sshInstanceResp === void 0 ? void 0 : sshInstanceResp.data.resource.resource.name) || 'ssh-instance';
57
+ (resp === null || resp === void 0 ? void 0 : resp.data.resource.resource.name) || 'ssh-instance';
83
58
  }
84
- const { resp, error } = await handler_1.runMutation({
85
- command: this,
86
- query: StartSSHSessionDocument,
87
- variables: {
88
- id: instanceId,
89
- accessLevel: cmd_1.DEFAULT_ACCESS_LEVEL,
90
- sessionId,
91
- },
92
- });
93
- switch (resp === null || resp === void 0 ? void 0 : resp.data.createSession.__typename) {
94
- case 'CreateSessionResult': {
95
- const metadata = resp.data.createSession.session.metadata;
96
- switch (metadata === null || metadata === void 0 ? void 0 : metadata.__typename) {
97
- case 'AwsIamFederatedSSMSession': {
98
- const updateAwsConfigCommand = aws_1.getAwsConfigUpdateCmd(instanceName, metadata.awsAccessKeyId, metadata.awsSecretAccessKey, metadata.awsSessionToken);
99
- const sessionCmd = `aws ssm start-session --target ${metadata.ec2InstanceId} --region ${metadata.ec2Region} --profile opal`;
100
- // TODO: Unfortunately allowing SSH over SSM disables logging
101
- // Run SSH script
102
- // const sessionCmd = `../../scripts/ssh_ssm.sh ${metadata.ec2InstanceId} ${flags.user} ${metadata.ec2Region}`
103
- const startSessionCmd = `${updateAwsConfigCommand} && ${sessionCmd}`;
104
- cmd_1.startInteractiveShell(startSessionCmd, `shell into ${instanceName ? `"${instanceName}" instance` : 'instance'}`);
105
- break;
106
- }
107
- default:
108
- apollo_1.printRequestOutput(this, resp, error);
109
- }
110
- break;
111
- }
112
- case 'MfaInvalidError': {
113
- common_1.handleMfaRedirect(this, instanceId);
114
- break;
115
- }
116
- case 'OidcIDTokenNotFoundError': {
117
- common_1.handleOidcRedirect(this, instanceId);
59
+ const session = await sessions_1.getOrCreateSession(this, instanceId, resources_1.DEFAULT_ACCESS_LEVEL, sessionId, exports.Ec2SessionMetadataFragment);
60
+ const metadata = session.metadata;
61
+ switch (metadata === null || metadata === void 0 ? void 0 : metadata.__typename) {
62
+ case 'AwsIamFederatedSSMSession': {
63
+ this.log(`\n🕰️ Connecting to a session that expires in ${sessions_1.getSessionExpirationMessage(session)}.`);
64
+ const updateAwsConfigCommand = aws_1.getAwsConfigUpdateCmd(instanceName, metadata.awsAccessKeyId, metadata.awsSecretAccessKey, metadata.awsSessionToken);
65
+ const sessionCmd = `aws ssm start-session --target ${metadata.ec2InstanceId} --region ${metadata.ec2Region} --profile opal`;
66
+ // TODO: Unfortunately allowing SSH over SSM disables logging
67
+ // Run SSH script
68
+ // const sessionCmd = `../../scripts/ssh_ssm.sh ${metadata.ec2InstanceId} ${flags.user} ${metadata.ec2Region}`
69
+ const startSessionCmd = `${updateAwsConfigCommand} && ${sessionCmd}`;
70
+ cmd_1.startInteractiveShell(startSessionCmd, `shell into ${instanceName ? `"${instanceName}" instance` : 'instance'}`);
118
71
  break;
119
72
  }
120
73
  default:
121
- apollo_1.printRequestOutput(this, resp, error);
74
+ return apollo_1.handleError(this, undefined, session);
122
75
  }
123
76
  }
124
77
  }
125
78
  exports.default = StartSSHSession;
126
- StartSSHSession.description = 'Start an SSH session to access a particular compute instance.';
79
+ StartSSHSession.description = 'Starts an SSH session to access a compute instance.';
127
80
  StartSSHSession.examples = [
128
81
  'opal ssh:start',
129
82
  'opal ssh:start --id 51f7176b-0464-4a6f-8369-e951e187b398',
package/lib/handler.d.ts CHANGED
@@ -12,5 +12,4 @@ export declare const runQuery: <TResponse = any, TVar = Record<string, any>>({ c
12
12
  resp: import("@apollo/client/core").ApolloQueryResult<TResponse> | null;
13
13
  error: any;
14
14
  }>;
15
- declare const handler: ({ command, query, variables }: QueryHandlerProps) => void;
16
- export default handler;
15
+ export {};
package/lib/handler.js CHANGED
@@ -37,30 +37,3 @@ exports.runQuery = async ({ command, query, variables, }) => {
37
37
  }
38
38
  return { resp: queryResp, error: queryError };
39
39
  };
40
- const handler = ({ command, query, variables }) => {
41
- const isMutation = query.includes('mutation');
42
- (async () => {
43
- let resp = null;
44
- let err = null;
45
- if (isMutation) {
46
- const { resp: mutationResp, error } = await exports.runMutation({
47
- command,
48
- query,
49
- variables,
50
- });
51
- resp = mutationResp;
52
- err = error;
53
- }
54
- else {
55
- const { resp: queryResp, error } = await exports.runQuery({
56
- command,
57
- query,
58
- variables,
59
- });
60
- resp = queryResp;
61
- err = error;
62
- }
63
- apollo_1.printRequestOutput(command, resp, err);
64
- })();
65
- };
66
- exports.default = handler;
@@ -2,4 +2,5 @@ import { ApolloClient, NormalizedCacheObject } from '@apollo/client/core';
2
2
  import { Command } from '@oclif/command';
3
3
  export declare let client: ApolloClient<NormalizedCacheObject> | null;
4
4
  export declare const initClient: (command: Command) => Promise<void>;
5
- export declare const printRequestOutput: (command: any, resp: any, err?: any) => void;
5
+ export declare const printResponse: (command: Command, resp: any) => void;
6
+ export declare const handleError: (command: Command, err: any, resp?: any) => void;
package/lib/lib/apollo.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.printRequestOutput = exports.initClient = exports.client = void 0;
3
+ exports.handleError = exports.printResponse = exports.initClient = exports.client = void 0;
4
4
  const core_1 = require("@apollo/client/core");
5
5
  const context_1 = require("@apollo/client/link/context");
6
6
  const error_1 = require("@apollo/client/link/error");
@@ -16,6 +16,7 @@ const fetch = require('node-fetch');
16
16
  const https = require('https');
17
17
  const http = require('http');
18
18
  exports.client = null;
19
+ let alreadyNotifiedRecommendedVersion = false;
19
20
  exports.initClient = async (command) => {
20
21
  const configDir = command.config.configDir;
21
22
  const currentCLIVersion = command.config.version;
@@ -43,7 +44,7 @@ exports.initClient = async (command) => {
43
44
  },
44
45
  });
45
46
  const authLink = context_1.setContext((_, { headers }) => {
46
- const baseHeaders = Object.assign(Object.assign({}, headers), { authorization: `Bearer ${accessToken}`, 'X-Opal-Organization-ID': organizationID });
47
+ const baseHeaders = Object.assign(Object.assign({}, headers), { authorization: `Bearer ${accessToken}`, 'X-Opal-Organization-ID': organizationID, 'X-Opal-Cli-Version': currentCLIVersion });
47
48
  if (customHeaderKey) {
48
49
  return {
49
50
  headers: Object.assign(Object.assign({}, baseHeaders), { [customHeaderKey]: customHeaderValue }),
@@ -59,16 +60,27 @@ exports.initClient = async (command) => {
59
60
  if (headers) {
60
61
  const expectedCLIMajorVersion = headers.get('Opal-CLI-Expected-Major-Version');
61
62
  if (expectedCLIMajorVersion) {
62
- const semvarExpectedMinCLIVersion = `${expectedCLIMajorVersion}.0.0`;
63
- command.log('');
64
- if (semver_1.major(currentCLIVersion) < semver_1.major(semvarExpectedMinCLIVersion)) {
65
- 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.
66
- Upgrade using the following command: ${chalk_1.default.blueBright('brew upgrade opalsecurity/brew/opal-security')}`));
63
+ const semverExpectedMinCLIVersion = `${expectedCLIMajorVersion}.0.0`;
64
+ if (semver_1.major(currentCLIVersion) < semver_1.major(semverExpectedMinCLIVersion)) {
65
+ command.warn(chalk_1.default.yellow(`
66
+ ❗️ Your CLI is outdated and is incompatible with your Opal server.
67
+ Expected major version ${chalk_1.default.blueBright(semverExpectedMinCLIVersion)}, but version ${chalk_1.default.blueBright(currentCLIVersion)} is installed.
68
+ Upgrade using the following command: ${chalk_1.default.blueBright('brew upgrade opalsecurity/brew/opal-security')}\n`));
67
69
  }
68
- else if (semver_1.major(currentCLIVersion) > semver_1.major(semvarExpectedMinCLIVersion)) {
69
- 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.'));
70
+ else if (semver_1.major(currentCLIVersion) > semver_1.major(semverExpectedMinCLIVersion)) {
71
+ command.warn(chalk_1.default.yellow(`
72
+ ❗️ Uh oh! The currently installed Opal server is outdated. Please contact your Opal admins to let them know they need to upgrade their deployment.\n`));
70
73
  }
71
- command.log('');
74
+ }
75
+ if (!alreadyNotifiedRecommendedVersion) {
76
+ const recommendedCLIMinorVersion = headers.get('Opal-CLI-Recommended-Version');
77
+ if (recommendedCLIMinorVersion && semver_1.compare(currentCLIVersion, recommendedCLIMinorVersion) < 0) {
78
+ command.log(chalk_1.default.yellow(`
79
+ ❗️ Opal recommends that you upgrade your CLI.
80
+ Recommended version >=${chalk_1.default.blueBright(recommendedCLIMinorVersion)}, but version ${chalk_1.default.blueBright(currentCLIVersion)} is installed.
81
+ Upgrade using the following command: ${chalk_1.default.blueBright('brew upgrade opalsecurity/brew/opal-security')}\n`));
82
+ }
83
+ alreadyNotifiedRecommendedVersion = true;
72
84
  }
73
85
  }
74
86
  };
@@ -97,7 +109,8 @@ exports.initClient = async (command) => {
97
109
  switch (networkError.statusCode) {
98
110
  case 401: {
99
111
  command.log('Your session is invalid or expired. Authenticating now...\n');
100
- login_1.default.run().then(() => {
112
+ const loginCommand = new login_1.default([], command.config);
113
+ loginCommand.run().then(() => {
101
114
  if (cmd_1.mostRecentCommandTime && cmd_1.mostRecentCommand) {
102
115
  const lastCommandReexecutionDuration = moment.duration(2, 'minutes');
103
116
  const lastCommandReexecutionDurationHasElapsed = cmd_1.mostRecentCommandTime.add(lastCommandReexecutionDuration) > moment(new Date());
@@ -110,6 +123,7 @@ exports.initClient = async (command) => {
110
123
  }
111
124
  default:
112
125
  command.log(`❗ Error: received status code ${networkError.statusCode} from server${errorMessage ? ` with message "${errorMessage}"` : ''}`);
126
+ process.exit(networkError.statusCode);
113
127
  }
114
128
  }
115
129
  });
@@ -121,19 +135,40 @@ exports.initClient = async (command) => {
121
135
  cache: new core_1.InMemoryCache(),
122
136
  });
123
137
  };
124
- exports.printRequestOutput = (command, resp, err) => {
125
- if (err && !('networkError' in err && 'statusCode' in err.networkError)) {
126
- if (err.toString().includes('self signed certificate')) {
127
- command.log('Request failed due to a self-signed certificate appearing in the certificate chain. Try setting your CLI to allow self-signed certs by running: "opal set-url --custom INSERT_URL_HERE --allowSelfSignedCerts"');
138
+ exports.printResponse = (command, resp) => {
139
+ const filteredJson = JSON.parse(JSON.stringify(resp.data, (k, v) => {
140
+ if (k === '__typename') {
141
+ return undefined;
128
142
  }
129
- else {
130
- // we handle status code errors in the global handler
131
- command.log(`❗ Error: ${err}`);
143
+ if (v === null || (Array.isArray(v) && v.length === 0)) {
144
+ return undefined;
132
145
  }
133
- }
134
- if (!resp) {
146
+ return v;
147
+ }));
148
+ command.log(prettyjson_1.render(filteredJson));
149
+ };
150
+ exports.handleError = (command, err, resp) => {
151
+ if (err && typeof err === 'object' && ('networkError' in err && 'statusCode' in err.networkError)) {
152
+ // Status code errors are already handled in the global Apollo handler, so we can just return here.
135
153
  return;
136
154
  }
137
- const filteredJson = JSON.parse(JSON.stringify(resp.data, (k, v) => (k === '__typename') ? undefined : v));
138
- command.log(prettyjson_1.render(filteredJson));
155
+ let errorMsg;
156
+ if (!err) {
157
+ errorMsg = 'Unexpected response from server (see below). Please contact Opal support if this issue persists.';
158
+ }
159
+ else if (typeof err === 'string') {
160
+ errorMsg = err;
161
+ }
162
+ else if (typeof err === 'object' && err.toString().includes('self signed certificate')) {
163
+ errorMsg = 'Request failed due to a self-signed certificate appearing in the certificate chain. Try setting your CLI to allow self-signed certs by running: "opal set-url --custom INSERT_URL_HERE --allowSelfSignedCerts"';
164
+ }
165
+ else {
166
+ errorMsg = `Error: ${err}`;
167
+ }
168
+ errorMsg = '❗ ' + errorMsg;
169
+ command.log(errorMsg);
170
+ if (resp) {
171
+ exports.printResponse(command, resp);
172
+ }
173
+ command.exit(1);
139
174
  };
package/lib/lib/aws.js CHANGED
@@ -42,8 +42,9 @@ exports.getAwsEnvVarMessage = () => {
42
42
  else if (process.env.AWS_DEFAULT_PROFILE !== opalProfileKey) {
43
43
  envVarPhrase = `set to "${process.env.AWS_DEFAULT_PROFILE}"`;
44
44
  }
45
- return '💡 Did you know you can set your AWS_DEFAULT_PROFILE environment ' +
46
- `variable to "${opalProfileKey}" to avoid explicitly specifying profile in each command? Currently, this variable is ${envVarPhrase}.`;
45
+ return '\n\n💡 Did you know you can set your AWS_DEFAULT_PROFILE environment ' +
46
+ `variable to "${opalProfileKey}" to avoid explicitly specifying profile in each command? ` +
47
+ `Currently, this variable is ${envVarPhrase}.`;
47
48
  }
48
49
  return '';
49
50
  };
package/lib/lib/cmd.d.ts CHANGED
@@ -1,17 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import { ExecException } from 'child_process';
3
- import { ResourceAccessLevelInput } from '../types';
4
3
  import { Command } from '@oclif/command';
5
4
  import * as moment from 'moment';
6
- export declare type ResourceInfo = {
7
- id: string;
8
- name: string;
9
- };
10
- export declare type AccessLevelInfo = {
11
- id: string;
12
- name: string;
13
- };
14
- export declare const DEFAULT_ACCESS_LEVEL: ResourceAccessLevelInput;
15
5
  export declare let mostRecentCommand: Command | null;
16
6
  export declare let mostRecentCommandTime: moment.Moment | null;
17
7
  export declare const setMostRecentCommand: (cmd: Command) => void;
@@ -19,4 +9,3 @@ export declare const startInteractiveShell: (runCmd: string, shellName?: string
19
9
  export declare const runCommandExec: (runCmd: string, successMessage: string, errorMessage: string, envVars?: Record<string, any> | undefined) => void;
20
10
  export declare const runCommandExecWithCallback: (cmd: string, callback?: ((error: ExecException | null, stdout: Buffer, stderr: Buffer) => void) | undefined) => Promise<unknown>;
21
11
  export declare const runCommandSpawn: (runCmd: string, successMessage: string, errorMessage: string, envVars?: Record<string, any> | undefined) => void;
22
- export declare const filterChoices: (input: string, choices: ResourceInfo[] | AccessLevelInfo[]) => ResourceInfo[];
package/lib/lib/cmd.js CHANGED
@@ -1,13 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.filterChoices = exports.runCommandSpawn = exports.runCommandExecWithCallback = exports.runCommandExec = exports.startInteractiveShell = exports.setMostRecentCommand = exports.mostRecentCommandTime = exports.mostRecentCommand = exports.DEFAULT_ACCESS_LEVEL = void 0;
3
+ exports.runCommandSpawn = exports.runCommandExecWithCallback = exports.runCommandExec = exports.startInteractiveShell = exports.setMostRecentCommand = exports.mostRecentCommandTime = exports.mostRecentCommand = void 0;
4
4
  const _ = require("lodash");
5
5
  const moment = require("moment");
6
6
  const { exec, spawn } = require('child_process');
7
- exports.DEFAULT_ACCESS_LEVEL = {
8
- accessLevelName: '',
9
- accessLevelRemoteId: '',
10
- };
11
7
  exports.mostRecentCommand = null;
12
8
  exports.mostRecentCommandTime = null;
13
9
  exports.setMostRecentCommand = (cmd) => {
@@ -46,7 +42,7 @@ exports.runCommandExec = (runCmd, successMessage, errorMessage, envVars) => {
46
42
  env: Object.assign(Object.assign(Object.assign({}, process.env), { SCRIPT_PATH: __dirname }), envVarsToUse),
47
43
  }, function (error, stdOut, stdErr) {
48
44
  if (error) {
49
- console.log(`❗ Error: ${_.upperFirst(errorMessage)}`);
45
+ console.log(`\n❗ Error: ${_.upperFirst(errorMessage)}`);
50
46
  }
51
47
  if (stdOut) {
52
48
  console.log(stdOut);
@@ -55,7 +51,7 @@ exports.runCommandExec = (runCmd, successMessage, errorMessage, envVars) => {
55
51
  console.log(stdErr);
56
52
  }
57
53
  if (!stdErr && !error) {
58
- console.log(`🎉 Success! ${_.upperFirst(successMessage)}`);
54
+ console.log(`\n🎉 Success! ${_.upperFirst(successMessage)}`);
59
55
  }
60
56
  });
61
57
  };
@@ -83,11 +79,3 @@ exports.runCommandSpawn = (runCmd, successMessage, errorMessage, envVars) => {
83
79
  }
84
80
  });
85
81
  };
86
- // Returns a filtered array of items that match the input
87
- exports.filterChoices = (input, choices) => {
88
- input = input || '';
89
- if (input === '') {
90
- return choices;
91
- }
92
- return choices.filter(choice => choice.name.toLowerCase().includes(input.toLowerCase()));
93
- };
@@ -1,3 +1,4 @@
1
- import { Command } from '@oclif/command';
2
- export declare const handleMfaRedirect: (command: Command, resourceId: string) => void;
3
- export declare const handleOidcRedirect: (command: Command, resourceId: string) => void;
1
+ /// <reference types="node" />
2
+ export declare const sleep: (ms: number) => Promise<unknown>;
3
+ export declare const copyToClipboard: (content: string) => import("child_process").ChildProcess;
4
+ export declare const displayContent: (content: string) => import("child_process").ChildProcess;
package/lib/lib/common.js CHANGED
@@ -1,20 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleOidcRedirect = exports.handleMfaRedirect = void 0;
4
- const config_1 = require("./config");
5
- const open = require("open");
6
- const handleResourceRedirect = (command, resourceId) => {
7
- const configData = config_1.getOrCreateConfigData(command.config.configDir);
8
- const url = configData[config_1.urlKey];
9
- setTimeout(() => {
10
- open(url + `/resources/${resourceId}?showModal=true`);
11
- }, 2000);
3
+ exports.displayContent = exports.copyToClipboard = exports.sleep = void 0;
4
+ const child_process_1 = require("child_process");
5
+ exports.sleep = (ms) => {
6
+ return new Promise(resolve => {
7
+ setTimeout(resolve, ms);
8
+ });
12
9
  };
13
- exports.handleMfaRedirect = (command, resourceId) => {
14
- command.log('❗ MFA validation needed. Please connect via browser. Redirecting...');
15
- handleResourceRedirect(command, resourceId);
10
+ const getCopyCommand = () => {
11
+ switch (process.platform) {
12
+ case 'darwin':
13
+ return 'pbcopy';
14
+ case 'win32':
15
+ return 'clip';
16
+ default:
17
+ return 'xclip -selection clipboard';
18
+ }
16
19
  };
17
- exports.handleOidcRedirect = (command, resourceId) => {
18
- command.log('❗ OIDC authentication needed. Please connect via browser. Redirecting...');
19
- handleResourceRedirect(command, resourceId);
20
+ exports.copyToClipboard = (content) => {
21
+ const copyCommand = getCopyCommand();
22
+ return child_process_1.exec(`echo "${content}" | ${copyCommand}`);
23
+ };
24
+ const getDisplayCommand = () => {
25
+ switch (process.platform) {
26
+ case 'win32':
27
+ return 'more';
28
+ default:
29
+ return 'less';
30
+ }
31
+ };
32
+ exports.displayContent = (content) => {
33
+ const displayCommand = getDisplayCommand();
34
+ return child_process_1.spawn(`cat <<< "${content}" | ${displayCommand}`, {
35
+ stdio: 'inherit',
36
+ shell: true,
37
+ });
20
38
  };
@@ -1,6 +1,14 @@
1
1
  import { Command } from '@oclif/command';
2
- import { ResourceInfo } from './cmd';
3
- import { ResourceAccessLevel } from '../types';
4
- export declare const resourcesAreEmpty: (context: Command, resources: ResourceInfo[]) => boolean;
5
- export declare const unexpectedAmountOfAccessLevelsFound: (context: Command) => void;
6
- export declare const promptUserForAccessLevels: (context: Command, resourceId: string, instanceType: string, accessLevelRemoteId?: string | undefined) => Promise<ResourceAccessLevel | null>;
2
+ import { ResourceAccessLevel, ResourceAccessLevelInput } from '../types';
3
+ export declare type ResourceInfo = {
4
+ id: string;
5
+ name: string;
6
+ };
7
+ export declare type AccessLevelInfo = {
8
+ id: string;
9
+ name: string;
10
+ };
11
+ export declare const DEFAULT_ACCESS_LEVEL: ResourceAccessLevelInput;
12
+ export declare const filterChoices: (input: string, choices: ResourceInfo[] | AccessLevelInfo[]) => ResourceInfo[];
13
+ export declare const promptUserForResource: (command: Command, resourceType: string, message: string) => Promise<ResourceInfo | void>;
14
+ export declare const promptUserForAccessLevels: (command: Command, resourceId: string, instanceType: string, accessLevelRemoteId?: string | undefined) => Promise<ResourceAccessLevel | void>;