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.
- package/README.md +24 -26
- package/lib/commands/iam-roles/start.js +30 -125
- package/lib/commands/kube-roles/start.js +23 -122
- package/lib/commands/login.d.ts +2 -1
- package/lib/commands/login.js +32 -23
- package/lib/commands/postgres-instances/start.js +59 -134
- package/lib/commands/resources/get.d.ts +1 -1
- package/lib/commands/resources/get.js +19 -1
- package/lib/commands/set-url.d.ts +6 -3
- package/lib/commands/set-url.js +36 -12
- package/lib/commands/ssh/copyFrom.js +18 -67
- package/lib/commands/ssh/copyTo.js +18 -67
- package/lib/commands/ssh/start.d.ts +1 -0
- package/lib/commands/ssh/start.js +33 -80
- package/lib/handler.d.ts +1 -2
- package/lib/handler.js +0 -27
- package/lib/lib/apollo.d.ts +2 -1
- package/lib/lib/apollo.js +57 -22
- package/lib/lib/aws.js +3 -2
- package/lib/lib/cmd.d.ts +0 -11
- package/lib/lib/cmd.js +3 -15
- package/lib/lib/common.d.ts +4 -3
- package/lib/lib/common.js +33 -15
- package/lib/lib/resources.d.ts +13 -5
- package/lib/lib/resources.js +83 -23
- package/lib/lib/sessions.d.ts +4 -0
- package/lib/lib/sessions.js +154 -0
- package/lib/lib/ssh.d.ts +1 -3
- package/lib/lib/ssh.js +3 -49
- package/lib/types.d.ts +1 -0
- package/oclif.manifest.json +1 -1
- package/package.json +2 -1
|
@@ -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
|
|
9
|
-
const
|
|
10
|
-
|
|
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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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.
|
|
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:
|
|
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
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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
|
|
78
|
-
apollo_1.
|
|
79
|
-
|
|
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
|
-
(
|
|
57
|
+
(resp === null || resp === void 0 ? void 0 : resp.data.resource.resource.name) || 'ssh-instance';
|
|
83
58
|
}
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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.
|
|
74
|
+
return apollo_1.handleError(this, undefined, session);
|
|
122
75
|
}
|
|
123
76
|
}
|
|
124
77
|
}
|
|
125
78
|
exports.default = StartSSHSession;
|
|
126
|
-
StartSSHSession.description = '
|
|
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
|
-
|
|
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;
|
package/lib/lib/apollo.d.ts
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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(
|
|
69
|
-
command.warn(chalk_1.default.yellow(
|
|
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
|
-
|
|
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
|
|
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.
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
command.log(`❗ Error: ${err}`);
|
|
143
|
+
if (v === null || (Array.isArray(v) && v.length === 0)) {
|
|
144
|
+
return undefined;
|
|
132
145
|
}
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
138
|
-
|
|
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?
|
|
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.
|
|
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(
|
|
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(
|
|
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
|
-
};
|
package/lib/lib/common.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
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.
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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.
|
|
18
|
-
|
|
19
|
-
|
|
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
|
};
|
package/lib/lib/resources.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { Command } from '@oclif/command';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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>;
|