opal-security 2.1.2 → 2.3.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 +233 -157
- package/bin/dev +6 -0
- package/bin/dev.cmd +3 -0
- package/bin/run +4 -4
- package/lib/commands/aws/identity.d.ts +2 -2
- package/lib/commands/aws/identity.js +5 -5
- package/lib/commands/curl-example.d.ts +2 -2
- package/lib/commands/curl-example.js +7 -6
- package/lib/commands/iam-roles/start.d.ts +6 -6
- package/lib/commands/iam-roles/start.js +19 -16
- package/lib/commands/kube-roles/start.d.ts +6 -6
- package/lib/commands/kube-roles/start.js +16 -13
- package/lib/commands/login.d.ts +4 -4
- package/lib/commands/login.js +28 -27
- package/lib/commands/logout.d.ts +3 -3
- package/lib/commands/logout.js +5 -5
- package/lib/commands/migrate-creds.d.ts +8 -0
- package/lib/commands/migrate-creds.js +48 -0
- package/lib/commands/postgres-instances/start.d.ts +7 -7
- package/lib/commands/postgres-instances/start.js +17 -14
- package/lib/commands/resources/get.d.ts +3 -3
- package/lib/commands/resources/get.js +8 -8
- package/lib/commands/set-custom-header.d.ts +4 -4
- package/lib/commands/set-custom-header.js +9 -9
- package/lib/commands/set-token.d.ts +3 -3
- package/lib/commands/set-token.js +10 -17
- package/lib/commands/set-url.d.ts +10 -12
- package/lib/commands/set-url.js +19 -20
- package/lib/commands/ssh/copyFrom.d.ts +7 -7
- package/lib/commands/ssh/copyFrom.js +16 -13
- package/lib/commands/ssh/copyTo.d.ts +7 -7
- package/lib/commands/ssh/copyTo.js +16 -13
- package/lib/commands/ssh/start.d.ts +5 -5
- package/lib/commands/ssh/start.js +19 -16
- package/lib/handler.d.ts +6 -5
- package/lib/handler.js +8 -6
- package/lib/index.d.ts +1 -1
- package/lib/index.js +3 -2
- package/lib/lib/apollo.d.ts +1 -1
- package/lib/lib/apollo.js +26 -19
- package/lib/lib/aws.js +10 -11
- package/lib/lib/cmd.d.ts +5 -4
- package/lib/lib/cmd.js +10 -5
- package/lib/lib/config.js +8 -5
- package/lib/lib/credentials/index.d.ts +11 -0
- package/lib/lib/credentials/index.js +52 -0
- package/lib/lib/credentials/keychain.d.ts +3 -0
- package/lib/lib/credentials/keychain.js +41 -0
- package/lib/lib/credentials/localEncryption.d.ts +3 -0
- package/lib/lib/credentials/localEncryption.js +75 -0
- package/lib/lib/flags.d.ts +5 -6
- package/lib/lib/flags.js +6 -6
- package/lib/lib/resources.d.ts +4 -4
- package/lib/lib/resources.js +14 -11
- package/lib/lib/sessions.d.ts +2 -2
- package/lib/lib/sessions.js +13 -11
- package/lib/lib/ssh.d.ts +1 -1
- package/lib/lib/ssh.js +6 -4
- package/lib/lib/util.js +8 -5
- package/lib/types.d.ts +1204 -1204
- package/lib/types.js +49 -49
- package/oclif.manifest.json +756 -1
- package/package.json +29 -24
- package/lib/lib/credentials.d.ts +0 -9
- package/lib/lib/credentials.js +0 -76
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Command } from '@oclif/
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
2
|
export declare const Ec2SessionMetadataFragment = "\n... on AwsIamFederatedSSMSession {\n awsAccessKeyId\n awsSecretAccessKey\n awsSessionToken\n awsLoginUrl\n federatedArn\n ec2InstanceId\n ec2Region\n}";
|
|
3
3
|
export default class StartSSHSession extends Command {
|
|
4
4
|
static description: string;
|
|
5
5
|
static examples: string[];
|
|
6
6
|
static flags: {
|
|
7
|
-
help: import("@oclif/
|
|
8
|
-
id: import("@oclif/
|
|
9
|
-
sessionId: import("@oclif/
|
|
10
|
-
refresh: import("@oclif/
|
|
7
|
+
help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
|
|
8
|
+
id: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
sessionId: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
refresh: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
11
|
};
|
|
12
12
|
run(): Promise<void>;
|
|
13
13
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Ec2SessionMetadataFragment = void 0;
|
|
4
|
-
const
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
5
|
const handler_1 = require("../../handler");
|
|
6
6
|
const cmd_1 = require("../../lib/cmd");
|
|
7
7
|
const apollo_1 = require("../../lib/apollo");
|
|
@@ -21,14 +21,14 @@ exports.Ec2SessionMetadataFragment = `
|
|
|
21
21
|
ec2InstanceId
|
|
22
22
|
ec2Region
|
|
23
23
|
}`;
|
|
24
|
-
class StartSSHSession extends
|
|
24
|
+
class StartSSHSession extends core_1.Command {
|
|
25
25
|
async run() {
|
|
26
|
-
cmd_1.setMostRecentCommand(this);
|
|
27
|
-
const { flags } = this.parse(StartSSHSession);
|
|
26
|
+
(0, cmd_1.setMostRecentCommand)(this);
|
|
27
|
+
const { flags } = await this.parse(StartSSHSession);
|
|
28
28
|
if (flags.sessionId && flags.refresh) {
|
|
29
|
-
return apollo_1.handleError(this, 'Cannot use both --sessionId and --refresh');
|
|
29
|
+
return (0, apollo_1.handleError)(this, 'Cannot use both --sessionId and --refresh');
|
|
30
30
|
}
|
|
31
|
-
const pluginExists = await ssh_1.assertSessionManagerPluginExists();
|
|
31
|
+
const pluginExists = await (0, ssh_1.assertSessionManagerPluginExists)();
|
|
32
32
|
if (!pluginExists) {
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
@@ -36,7 +36,7 @@ class StartSSHSession extends command_1.Command {
|
|
|
36
36
|
let instanceName = null;
|
|
37
37
|
const sessionId = flags.sessionId;
|
|
38
38
|
if (!instanceId) {
|
|
39
|
-
const selectedInstance = await ssh_1.selectComputeInstance(this, 'SSH into');
|
|
39
|
+
const selectedInstance = await (0, ssh_1.selectComputeInstance)(this, 'SSH into');
|
|
40
40
|
if (!selectedInstance) {
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
@@ -44,7 +44,7 @@ class StartSSHSession extends command_1.Command {
|
|
|
44
44
|
instanceName = selectedInstance.name;
|
|
45
45
|
}
|
|
46
46
|
else {
|
|
47
|
-
const { resp, error } = await handler_1.runQuery({
|
|
47
|
+
const { resp, error } = await (0, handler_1.runQuery)({
|
|
48
48
|
command: this,
|
|
49
49
|
query: get_1.GetResourceDocument,
|
|
50
50
|
variables: {
|
|
@@ -52,34 +52,36 @@ class StartSSHSession extends command_1.Command {
|
|
|
52
52
|
},
|
|
53
53
|
});
|
|
54
54
|
if (error) {
|
|
55
|
-
return apollo_1.handleError(this, error, resp);
|
|
55
|
+
return (0, apollo_1.handleError)(this, error, resp);
|
|
56
56
|
}
|
|
57
57
|
if (!(resp === null || resp === void 0 ? void 0 : resp.data.resource.resource)) {
|
|
58
|
-
return apollo_1.handleError(this, `Resource not found for ID: ${instanceId}`);
|
|
58
|
+
return (0, apollo_1.handleError)(this, `Resource not found for ID: ${instanceId}`);
|
|
59
59
|
}
|
|
60
60
|
instanceName =
|
|
61
61
|
(resp === null || resp === void 0 ? void 0 : resp.data.resource.resource.name) || 'ssh-instance';
|
|
62
62
|
}
|
|
63
|
-
const session = await sessions_1.getOrCreateSession(this, instanceId, resources_1.DEFAULT_ACCESS_LEVEL, sessionId, exports.Ec2SessionMetadataFragment, flags.refresh);
|
|
63
|
+
const session = await (0, sessions_1.getOrCreateSession)(this, instanceId, resources_1.DEFAULT_ACCESS_LEVEL, sessionId, exports.Ec2SessionMetadataFragment, flags.refresh);
|
|
64
|
+
if (!session) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
64
67
|
const metadata = session.metadata;
|
|
65
68
|
switch (metadata === null || metadata === void 0 ? void 0 : metadata.__typename) {
|
|
66
69
|
case 'AwsIamFederatedSSMSession': {
|
|
67
|
-
this.log(`\n🕰️ Connecting to a session that expires in ${sessions_1.getSessionExpirationMessage(session)}.`);
|
|
68
|
-
const updateAwsConfigCommand = aws_1.getAwsConfigUpdateCmd(instanceName, metadata.awsAccessKeyId, metadata.awsSecretAccessKey, metadata.awsSessionToken);
|
|
70
|
+
this.log(`\n🕰️ Connecting to a session that expires in ${(0, sessions_1.getSessionExpirationMessage)(session)}.`);
|
|
71
|
+
const updateAwsConfigCommand = (0, aws_1.getAwsConfigUpdateCmd)(instanceName, metadata.awsAccessKeyId, metadata.awsSecretAccessKey, metadata.awsSessionToken);
|
|
69
72
|
const sessionCmd = `aws ssm start-session --target ${metadata.ec2InstanceId} --region ${metadata.ec2Region} --profile opal`;
|
|
70
73
|
// TODO: Unfortunately allowing SSH over SSM disables logging
|
|
71
74
|
// Run SSH script
|
|
72
75
|
// const sessionCmd = `../../scripts/ssh_ssm.sh ${metadata.ec2InstanceId} ${flags.user} ${metadata.ec2Region}`
|
|
73
76
|
const startSessionCmd = `${updateAwsConfigCommand} && ${sessionCmd}`;
|
|
74
|
-
cmd_1.startInteractiveShell(startSessionCmd, `shell into ${instanceName ? `"${instanceName}" instance` : 'instance'}`);
|
|
77
|
+
(0, cmd_1.startInteractiveShell)(startSessionCmd, `shell into ${instanceName ? `"${instanceName}" instance` : 'instance'}`);
|
|
75
78
|
break;
|
|
76
79
|
}
|
|
77
80
|
default:
|
|
78
|
-
return apollo_1.handleError(this, undefined, session);
|
|
81
|
+
return (0, apollo_1.handleError)(this, undefined, session);
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
|
-
exports.default = StartSSHSession;
|
|
83
85
|
StartSSHSession.description = 'Starts an SSH session to access a compute instance.';
|
|
84
86
|
StartSSHSession.examples = [
|
|
85
87
|
'opal ssh:start',
|
|
@@ -97,3 +99,4 @@ StartSSHSession.flags = {
|
|
|
97
99
|
sessionId: flags_1.SHARED_FLAGS.sessionId,
|
|
98
100
|
refresh: flags_1.SHARED_FLAGS.refresh,
|
|
99
101
|
};
|
|
102
|
+
exports.default = StartSSHSession;
|
package/lib/handler.d.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { Command } from '@oclif/
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { OperationVariables, ApolloError } from '@apollo/client/core';
|
|
2
3
|
interface QueryHandlerProps<TVar = Record<string, any>> {
|
|
3
4
|
command: Command;
|
|
4
5
|
query: string;
|
|
5
6
|
variables?: TVar;
|
|
6
7
|
}
|
|
7
8
|
export declare const runMutation: ({ command, query, variables, }: QueryHandlerProps) => Promise<{
|
|
8
|
-
resp: import("@apollo/client/core").FetchResult<any
|
|
9
|
-
error:
|
|
9
|
+
resp: import("@apollo/client/core").FetchResult<any> | null;
|
|
10
|
+
error: unknown;
|
|
10
11
|
}>;
|
|
11
|
-
export declare const runQuery: <TResponse = any, TVar = Record<string, any>>({ command, query, variables, }: QueryHandlerProps<TVar>) => Promise<{
|
|
12
|
+
export declare const runQuery: <TResponse = any, TVar extends OperationVariables = Record<string, any>>({ command, query, variables, }: QueryHandlerProps<TVar>) => Promise<{
|
|
12
13
|
resp: import("@apollo/client/core").ApolloQueryResult<TResponse> | null;
|
|
13
|
-
error:
|
|
14
|
+
error: ApolloError;
|
|
14
15
|
}>;
|
|
15
16
|
export {};
|
package/lib/handler.js
CHANGED
|
@@ -3,13 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.runQuery = exports.runMutation = void 0;
|
|
4
4
|
const graphql_tag_1 = require("graphql-tag");
|
|
5
5
|
const apollo_1 = require("./lib/apollo");
|
|
6
|
-
|
|
7
|
-
await apollo_1.initClient(command);
|
|
6
|
+
const runMutation = async ({ command, query, variables, }) => {
|
|
7
|
+
await (0, apollo_1.initClient)(command);
|
|
8
8
|
let mutationResp = null;
|
|
9
9
|
let mutationError = null;
|
|
10
10
|
try {
|
|
11
11
|
mutationResp = await apollo_1.client.mutate({
|
|
12
|
-
mutation: graphql_tag_1.default `
|
|
12
|
+
mutation: (0, graphql_tag_1.default) `
|
|
13
13
|
${query}
|
|
14
14
|
`,
|
|
15
15
|
variables: variables,
|
|
@@ -20,13 +20,14 @@ exports.runMutation = async ({ command, query, variables, }) => {
|
|
|
20
20
|
}
|
|
21
21
|
return { resp: mutationResp, error: mutationError };
|
|
22
22
|
};
|
|
23
|
-
exports.
|
|
24
|
-
|
|
23
|
+
exports.runMutation = runMutation;
|
|
24
|
+
const runQuery = async ({ command, query, variables, }) => {
|
|
25
|
+
await (0, apollo_1.initClient)(command);
|
|
25
26
|
let queryResp = null;
|
|
26
27
|
let queryError = null;
|
|
27
28
|
try {
|
|
28
29
|
queryResp = await apollo_1.client.query({
|
|
29
|
-
query: graphql_tag_1.default `
|
|
30
|
+
query: (0, graphql_tag_1.default) `
|
|
30
31
|
${query}
|
|
31
32
|
`,
|
|
32
33
|
variables: variables,
|
|
@@ -37,3 +38,4 @@ exports.runQuery = async ({ command, query, variables, }) => {
|
|
|
37
38
|
}
|
|
38
39
|
return { resp: queryResp, error: queryError };
|
|
39
40
|
};
|
|
41
|
+
exports.runQuery = runQuery;
|
package/lib/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { run } from '@oclif/
|
|
1
|
+
export { run } from '@oclif/core';
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
exports.run = void 0;
|
|
4
|
+
var core_1 = require("@oclif/core");
|
|
5
|
+
Object.defineProperty(exports, "run", { enumerable: true, get: function () { return core_1.run; } });
|
package/lib/lib/apollo.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApolloClient, NormalizedCacheObject } from '@apollo/client/core';
|
|
2
|
-
import { Command } from '@oclif/
|
|
2
|
+
import { Command } from '@oclif/core';
|
|
3
3
|
export declare let client: ApolloClient<NormalizedCacheObject> | null;
|
|
4
4
|
export declare const printResponse: (command: Command, resp: any) => void;
|
|
5
5
|
export declare const handleError: (command: Command, err: any, resp?: any) => void;
|
package/lib/lib/apollo.js
CHANGED
|
@@ -17,16 +17,17 @@ const https = require('https');
|
|
|
17
17
|
const http = require('http');
|
|
18
18
|
exports.client = null;
|
|
19
19
|
let alreadyNotifiedRecommendedVersion = false;
|
|
20
|
-
|
|
20
|
+
const printResponse = (command, resp) => {
|
|
21
21
|
const filteredJson = JSON.parse(JSON.stringify(resp.data, (k, v) => {
|
|
22
22
|
if (k === '__typename' || v === null || (Array.isArray(v) && v.length === 0)) {
|
|
23
23
|
return undefined;
|
|
24
24
|
}
|
|
25
25
|
return v;
|
|
26
26
|
}));
|
|
27
|
-
command.log(prettyjson_1.render(filteredJson));
|
|
27
|
+
command.log((0, prettyjson_1.render)(filteredJson));
|
|
28
28
|
};
|
|
29
|
-
exports.
|
|
29
|
+
exports.printResponse = printResponse;
|
|
30
|
+
const handleError = (command, err, resp) => {
|
|
30
31
|
if (err && typeof err === 'object' && ('networkError' in err && 'statusCode' in err.networkError)) {
|
|
31
32
|
// Status code errors are already handled in the global Apollo handler, so we can just return here.
|
|
32
33
|
return;
|
|
@@ -44,18 +45,20 @@ exports.handleError = (command, err, resp) => {
|
|
|
44
45
|
errorMsg = '❗ ' + errorMsg;
|
|
45
46
|
command.log(errorMsg);
|
|
46
47
|
if (resp) {
|
|
47
|
-
exports.printResponse(command, resp);
|
|
48
|
+
(0, exports.printResponse)(command, resp);
|
|
48
49
|
}
|
|
49
50
|
// OPAL-6579: Use process.exit to avoid UnhandledPromiseRejectionWarning
|
|
50
51
|
// eslint-disable-next-line no-process-exit,unicorn/no-process-exit
|
|
51
52
|
process.exit(1);
|
|
52
53
|
};
|
|
53
|
-
exports.
|
|
54
|
+
exports.handleError = handleError;
|
|
55
|
+
const initClient = async (command) => {
|
|
54
56
|
const configDir = command.config.configDir;
|
|
55
57
|
const currentCLIVersion = command.config.version;
|
|
56
|
-
const configData = config_1.getOrCreateConfigData(configDir);
|
|
57
|
-
const
|
|
58
|
-
const
|
|
58
|
+
const configData = (0, config_1.getOrCreateConfigData)(configDir);
|
|
59
|
+
const opalCreds = await (0, credentials_1.getOpalCredentials)(command);
|
|
60
|
+
const accessToken = opalCreds === null || opalCreds === void 0 ? void 0 : opalCreds.accessToken;
|
|
61
|
+
const organizationID = opalCreds === null || opalCreds === void 0 ? void 0 : opalCreds.organizationID;
|
|
59
62
|
const httpsAgent = new https.Agent({
|
|
60
63
|
rejectUnauthorized: !configData[config_1.allowSelfSignedCertsKey],
|
|
61
64
|
});
|
|
@@ -69,14 +72,14 @@ exports.initClient = async (command) => {
|
|
|
69
72
|
'' :
|
|
70
73
|
customHeader.split(':')[1];
|
|
71
74
|
const agent = specifiedUrl.includes('https') ? httpsAgent : httpAgent;
|
|
72
|
-
const httpLink = core_1.createHttpLink({
|
|
75
|
+
const httpLink = (0, core_1.createHttpLink)({
|
|
73
76
|
uri: `${specifiedUrl}/query`,
|
|
74
77
|
fetch,
|
|
75
78
|
fetchOptions: {
|
|
76
79
|
agent: agent,
|
|
77
80
|
},
|
|
78
81
|
});
|
|
79
|
-
const authLink = context_1.setContext((_, { headers }) => {
|
|
82
|
+
const authLink = (0, context_1.setContext)((_, { headers }) => {
|
|
80
83
|
const baseHeaders = Object.assign(Object.assign({}, headers), { authorization: `Bearer ${accessToken}`, 'X-Opal-Organization-ID': organizationID, 'X-Opal-Cli-Version': currentCLIVersion });
|
|
81
84
|
if (customHeaderKey) {
|
|
82
85
|
return {
|
|
@@ -94,20 +97,20 @@ exports.initClient = async (command) => {
|
|
|
94
97
|
const expectedCLIMajorVersion = headers.get('Opal-CLI-Expected-Major-Version');
|
|
95
98
|
if (expectedCLIMajorVersion) {
|
|
96
99
|
const semverExpectedMinCLIVersion = `${expectedCLIMajorVersion}.0.0`;
|
|
97
|
-
if (semver_1.major(currentCLIVersion) < semver_1.major(semverExpectedMinCLIVersion)) {
|
|
100
|
+
if ((0, semver_1.major)(currentCLIVersion) < (0, semver_1.major)(semverExpectedMinCLIVersion)) {
|
|
98
101
|
command.warn(chalk_1.default.yellow(`
|
|
99
102
|
❗️ Your CLI is outdated and is incompatible with your Opal server.
|
|
100
103
|
Expected major version ${chalk_1.default.blueBright(semverExpectedMinCLIVersion)}, but version ${chalk_1.default.blueBright(currentCLIVersion)} is installed.
|
|
101
104
|
Upgrade using the following command: ${chalk_1.default.blueBright('brew upgrade opalsecurity/brew/opal-security')}\n`));
|
|
102
105
|
}
|
|
103
|
-
else if (semver_1.major(currentCLIVersion) > semver_1.major(semverExpectedMinCLIVersion)) {
|
|
106
|
+
else if ((0, semver_1.major)(currentCLIVersion) > (0, semver_1.major)(semverExpectedMinCLIVersion)) {
|
|
104
107
|
command.warn(chalk_1.default.yellow(`
|
|
105
108
|
❗️ 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`));
|
|
106
109
|
}
|
|
107
110
|
}
|
|
108
111
|
if (!alreadyNotifiedRecommendedVersion) {
|
|
109
112
|
const recommendedCLIMinorVersion = headers.get('Opal-CLI-Recommended-Version');
|
|
110
|
-
if (recommendedCLIMinorVersion && semver_1.compare(currentCLIVersion, recommendedCLIMinorVersion) < 0) {
|
|
113
|
+
if (recommendedCLIMinorVersion && (0, semver_1.compare)(currentCLIVersion, recommendedCLIMinorVersion) < 0) {
|
|
111
114
|
command.log(chalk_1.default.yellow(`
|
|
112
115
|
❗️ Opal recommends that you upgrade your CLI.
|
|
113
116
|
Recommended version >=${chalk_1.default.blueBright(recommendedCLIMinorVersion)}, but version ${chalk_1.default.blueBright(currentCLIVersion)} is installed.
|
|
@@ -123,8 +126,8 @@ exports.initClient = async (command) => {
|
|
|
123
126
|
return response;
|
|
124
127
|
});
|
|
125
128
|
});
|
|
126
|
-
const errorLink = error_1.onError(({ networkError, operation }) => {
|
|
127
|
-
var _a
|
|
129
|
+
const errorLink = (0, error_1.onError)(({ networkError, operation }) => {
|
|
130
|
+
var _a;
|
|
128
131
|
if (operation.operationName === login_1.CLIAuthSessionCheckName) {
|
|
129
132
|
// These operations are triggered after the user just called `opal login` or `opal set-token`,
|
|
130
133
|
// so we have special handling if they fail and we don't want to trigger an automatic redirect
|
|
@@ -139,9 +142,12 @@ exports.initClient = async (command) => {
|
|
|
139
142
|
if (networkError && 'statusCode' in networkError) {
|
|
140
143
|
let errorMessage = null;
|
|
141
144
|
if ('result' in networkError) {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
+
const result = networkError.result;
|
|
146
|
+
// result is now of type string | Record<string, any>
|
|
147
|
+
if (typeof result === "string")
|
|
148
|
+
errorMessage = result;
|
|
149
|
+
else if (result.length > 0) {
|
|
150
|
+
errorMessage = (_a = result[0]) === null || _a === void 0 ? void 0 : _a.message;
|
|
145
151
|
}
|
|
146
152
|
}
|
|
147
153
|
switch (networkError.statusCode) {
|
|
@@ -160,7 +166,7 @@ exports.initClient = async (command) => {
|
|
|
160
166
|
break;
|
|
161
167
|
}
|
|
162
168
|
default:
|
|
163
|
-
return exports.handleError(command, `Received status code ${networkError.statusCode} from server${errorMessage ? ` with message "${errorMessage}"` : ''}`);
|
|
169
|
+
return (0, exports.handleError)(command, `Received status code ${networkError.statusCode} from server${errorMessage ? ` with message "${errorMessage}"` : ''}`);
|
|
164
170
|
}
|
|
165
171
|
}
|
|
166
172
|
});
|
|
@@ -172,3 +178,4 @@ exports.initClient = async (command) => {
|
|
|
172
178
|
cache: new core_1.InMemoryCache(),
|
|
173
179
|
});
|
|
174
180
|
};
|
|
181
|
+
exports.initClient = initClient;
|
package/lib/lib/aws.js
CHANGED
|
@@ -2,21 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getAwsEnvVarMessage = exports.getAwsConfigUpdateCmd = void 0;
|
|
4
4
|
const opalProfileKey = 'opal';
|
|
5
|
-
const kebabCase = (str) => {
|
|
6
|
-
return str.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[\s_]+/g, '-').toLowerCase();
|
|
7
|
-
};
|
|
8
5
|
const sanitize = (str) => {
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
//
|
|
6
|
+
// Remove non standard characters.
|
|
7
|
+
// Note: AWS supports profile names containing alphanumeric characters, symbols, and white spaces
|
|
8
|
+
// from the ASCII character set.
|
|
9
|
+
let sanitizedStr = str.replace(/[^A-Za-z0-9-_ ]/g, '');
|
|
10
|
+
// De-dupe spaces
|
|
12
11
|
sanitizedStr = sanitizedStr.replace(/\s+/g, ' ');
|
|
13
|
-
//
|
|
12
|
+
// Map remaining spaces to '-'
|
|
14
13
|
sanitizedStr = sanitizedStr.replace(/ /g, '-');
|
|
15
|
-
// convert to kebab case
|
|
16
|
-
sanitizedStr = kebabCase(sanitizedStr);
|
|
17
14
|
return sanitizedStr;
|
|
18
15
|
};
|
|
19
|
-
|
|
16
|
+
const getAwsConfigUpdateCmd = (itemName, awsAccessKeyId, awsSecretAccessKey, awsSessionToken) => {
|
|
20
17
|
let updateProfilesCmd = '';
|
|
21
18
|
const sanitizedProfileName = sanitize(itemName);
|
|
22
19
|
const profileNames = [opalProfileKey, sanitizedProfileName];
|
|
@@ -33,7 +30,8 @@ exports.getAwsConfigUpdateCmd = (itemName, awsAccessKeyId, awsSecretAccessKey, a
|
|
|
33
30
|
});
|
|
34
31
|
return updateProfilesCmd;
|
|
35
32
|
};
|
|
36
|
-
exports.
|
|
33
|
+
exports.getAwsConfigUpdateCmd = getAwsConfigUpdateCmd;
|
|
34
|
+
const getAwsEnvVarMessage = () => {
|
|
37
35
|
if (!process.env.AWS_DEFAULT_PROFILE || process.env.AWS_DEFAULT_PROFILE !== opalProfileKey) {
|
|
38
36
|
let envVarPhrase = '';
|
|
39
37
|
if (!process.env.AWS_DEFAULT_PROFILE) {
|
|
@@ -48,3 +46,4 @@ exports.getAwsEnvVarMessage = () => {
|
|
|
48
46
|
}
|
|
49
47
|
return '';
|
|
50
48
|
};
|
|
49
|
+
exports.getAwsEnvVarMessage = getAwsEnvVarMessage;
|
package/lib/lib/cmd.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import { ExecException } from 'child_process';
|
|
3
|
-
import { Command } from '@oclif/
|
|
4
|
+
import { Command } from '@oclif/core';
|
|
4
5
|
import * as moment from 'moment';
|
|
5
6
|
export declare let mostRecentCommand: Command | null;
|
|
6
7
|
export declare let mostRecentCommandTime: moment.Moment | null;
|
|
7
8
|
export declare const setMostRecentCommand: (cmd: Command) => void;
|
|
8
|
-
export declare const startInteractiveShell: (runCmd: string, shellName?: string
|
|
9
|
-
export declare const runCommandExec: (runCmd: string, successMessage: string, errorMessage: string, envVars?: Record<string, any>
|
|
9
|
+
export declare const startInteractiveShell: (runCmd: string, shellName?: string) => void;
|
|
10
|
+
export declare const runCommandExec: (runCmd: string, successMessage: string, errorMessage: string, envVars?: Record<string, any>) => void;
|
|
10
11
|
export declare const runCommandExecWithCallback: (cmd: string, callback?: ((error: ExecException | null, stdout: Buffer, stderr: Buffer) => void) | undefined) => Promise<unknown>;
|
|
11
|
-
export declare const runCommandSpawn: (runCmd: string, successMessage: string, errorMessage: string, envVars?: Record<string, any>
|
|
12
|
+
export declare const runCommandSpawn: (runCmd: string, successMessage: string, errorMessage: string, envVars?: Record<string, any>) => void;
|
package/lib/lib/cmd.js
CHANGED
|
@@ -6,11 +6,12 @@ const moment = require("moment");
|
|
|
6
6
|
const { exec, spawn } = require('child_process');
|
|
7
7
|
exports.mostRecentCommand = null;
|
|
8
8
|
exports.mostRecentCommandTime = null;
|
|
9
|
-
|
|
9
|
+
const setMostRecentCommand = (cmd) => {
|
|
10
10
|
exports.mostRecentCommand = cmd;
|
|
11
11
|
exports.mostRecentCommandTime = moment(new Date());
|
|
12
12
|
};
|
|
13
|
-
exports.
|
|
13
|
+
exports.setMostRecentCommand = setMostRecentCommand;
|
|
14
|
+
const startInteractiveShell = (runCmd, shellName) => {
|
|
14
15
|
const shell = spawn(`$SHELL -c "${runCmd}"`, [], {
|
|
15
16
|
env: Object.assign(Object.assign({}, process.env), { SCRIPT_PATH: __dirname }),
|
|
16
17
|
stdio: 'inherit',
|
|
@@ -36,7 +37,8 @@ exports.startInteractiveShell = (runCmd, shellName) => {
|
|
|
36
37
|
// intercepting SIGTSTP
|
|
37
38
|
});
|
|
38
39
|
};
|
|
39
|
-
exports.
|
|
40
|
+
exports.startInteractiveShell = startInteractiveShell;
|
|
41
|
+
const runCommandExec = (runCmd, successMessage, errorMessage, envVars) => {
|
|
40
42
|
const envVarsToUse = envVars || {};
|
|
41
43
|
exec(`${runCmd}`, {
|
|
42
44
|
env: Object.assign(Object.assign(Object.assign({}, process.env), { SCRIPT_PATH: __dirname }), envVarsToUse),
|
|
@@ -55,7 +57,8 @@ exports.runCommandExec = (runCmd, successMessage, errorMessage, envVars) => {
|
|
|
55
57
|
}
|
|
56
58
|
});
|
|
57
59
|
};
|
|
58
|
-
exports.
|
|
60
|
+
exports.runCommandExec = runCommandExec;
|
|
61
|
+
const runCommandExecWithCallback = (cmd, callback) => {
|
|
59
62
|
return new Promise(resolve => {
|
|
60
63
|
exec(cmd, function (error, stdOut, stdErr) {
|
|
61
64
|
callback && callback(error, stdOut, stdErr);
|
|
@@ -63,7 +66,8 @@ exports.runCommandExecWithCallback = (cmd, callback) => {
|
|
|
63
66
|
});
|
|
64
67
|
});
|
|
65
68
|
};
|
|
66
|
-
exports.
|
|
69
|
+
exports.runCommandExecWithCallback = runCommandExecWithCallback;
|
|
70
|
+
const runCommandSpawn = (runCmd, successMessage, errorMessage, envVars) => {
|
|
67
71
|
const envVarsToUse = envVars || {};
|
|
68
72
|
const shell = spawn(`${runCmd}`, [], {
|
|
69
73
|
env: Object.assign(Object.assign(Object.assign({}, process.env), { SCRIPT_PATH: __dirname }), envVarsToUse),
|
|
@@ -79,3 +83,4 @@ exports.runCommandSpawn = (runCmd, successMessage, errorMessage, envVars) => {
|
|
|
79
83
|
}
|
|
80
84
|
});
|
|
81
85
|
};
|
|
86
|
+
exports.runCommandSpawn = runCommandSpawn;
|
package/lib/lib/config.js
CHANGED
|
@@ -8,7 +8,7 @@ exports.defaultUrl = 'https://app.opal.dev';
|
|
|
8
8
|
exports.allowSelfSignedCertsKey = 'allowSelfSignedCerts';
|
|
9
9
|
exports.defaultAllowSelfSignedCerts = false;
|
|
10
10
|
exports.customHttpHeaderKey = 'customHttpHeader';
|
|
11
|
-
|
|
11
|
+
const getOrCreateConfigData = (configDir) => {
|
|
12
12
|
if (!fs.existsSync(configDir)) {
|
|
13
13
|
fs.mkdirSync(configDir, { recursive: true });
|
|
14
14
|
}
|
|
@@ -31,8 +31,9 @@ exports.getOrCreateConfigData = (configDir) => {
|
|
|
31
31
|
}
|
|
32
32
|
return configData;
|
|
33
33
|
};
|
|
34
|
-
exports.
|
|
35
|
-
|
|
34
|
+
exports.getOrCreateConfigData = getOrCreateConfigData;
|
|
35
|
+
const writeConfigData = (configDir, newConfigData) => {
|
|
36
|
+
const existingData = (0, exports.getOrCreateConfigData)(configDir);
|
|
36
37
|
for (const [key, value] of Object.entries(newConfigData)) {
|
|
37
38
|
existingData[key] = value;
|
|
38
39
|
}
|
|
@@ -41,8 +42,9 @@ exports.writeConfigData = (configDir, newConfigData) => {
|
|
|
41
42
|
mode: 0o0600,
|
|
42
43
|
});
|
|
43
44
|
};
|
|
44
|
-
exports.
|
|
45
|
-
|
|
45
|
+
exports.writeConfigData = writeConfigData;
|
|
46
|
+
const isProduction = (configDir) => {
|
|
47
|
+
const configData = (0, exports.getOrCreateConfigData)(configDir);
|
|
46
48
|
// Custom URLs are considered production since it includes on-prem
|
|
47
49
|
return configData[exports.urlKey] !== 'https://dev.opal.dev' &&
|
|
48
50
|
configData[exports.urlKey] !== 'http://localhost:3000' &&
|
|
@@ -50,3 +52,4 @@ exports.isProduction = (configDir) => {
|
|
|
50
52
|
configData[exports.urlKey] !== 'https://demo.opal.dev' &&
|
|
51
53
|
configData[exports.urlKey] !== 'https://staging.opal.dev';
|
|
52
54
|
};
|
|
55
|
+
exports.isProduction = isProduction;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
interface OpalCredentials {
|
|
3
|
+
email?: string;
|
|
4
|
+
organizationID?: string;
|
|
5
|
+
clientIDCandidate?: string;
|
|
6
|
+
accessToken?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const setOpalCredentials: (command: Command, email: string | undefined, organizationID: string, clientIDCandidate: string | undefined | null, accessToken: string) => Promise<void>;
|
|
9
|
+
export declare const getOpalCredentials: (command: Command) => Promise<OpalCredentials>;
|
|
10
|
+
export declare const removeOpalCredentials: (command: Command) => Promise<void>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.removeOpalCredentials = exports.getOpalCredentials = exports.setOpalCredentials = void 0;
|
|
4
|
+
const config_1 = require("../config");
|
|
5
|
+
const keychain_1 = require("./keychain");
|
|
6
|
+
const localEncryption_1 = require("./localEncryption");
|
|
7
|
+
const setOpalCredentials = async (command, email, organizationID, clientIDCandidate, accessToken) => {
|
|
8
|
+
email = email || 'email-unset';
|
|
9
|
+
const configData = (0, config_1.getOrCreateConfigData)(command.config.configDir);
|
|
10
|
+
configData.creds = {
|
|
11
|
+
clientIDCandidate,
|
|
12
|
+
email,
|
|
13
|
+
organizationID,
|
|
14
|
+
};
|
|
15
|
+
(0, config_1.writeConfigData)(command.config.configDir, configData);
|
|
16
|
+
if (process.platform === "darwin") {
|
|
17
|
+
await (0, keychain_1.setTokenInKeychain)(email, accessToken);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
await (0, localEncryption_1.setTokenInConfig)(command, configData, accessToken);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
exports.setOpalCredentials = setOpalCredentials;
|
|
24
|
+
const getOpalCredentials = async (command) => {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
let creds = (_b = (_a = (0, config_1.getOrCreateConfigData)(command.config.configDir)) === null || _a === void 0 ? void 0 : _a.creds) !== null && _b !== void 0 ? _b : {};
|
|
27
|
+
let accessToken = null;
|
|
28
|
+
if (process.platform === "darwin") {
|
|
29
|
+
accessToken = await (0, keychain_1.getTokenFromKeychain)((creds === null || creds === void 0 ? void 0 : creds.email) || 'email-unset');
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
accessToken = await (0, localEncryption_1.getTokenFromConfig)(creds);
|
|
33
|
+
}
|
|
34
|
+
if (accessToken) {
|
|
35
|
+
creds.accessToken = accessToken;
|
|
36
|
+
}
|
|
37
|
+
return creds;
|
|
38
|
+
};
|
|
39
|
+
exports.getOpalCredentials = getOpalCredentials;
|
|
40
|
+
const removeOpalCredentials = async (command) => {
|
|
41
|
+
var _a;
|
|
42
|
+
const configData = (0, config_1.getOrCreateConfigData)(command.config.configDir);
|
|
43
|
+
const email = ((_a = configData === null || configData === void 0 ? void 0 : configData.creds) === null || _a === void 0 ? void 0 : _a.email) || 'email-unset';
|
|
44
|
+
// On linux, the access token is stored encrypted in configData.creds, so this effectively removes it
|
|
45
|
+
configData.creds = {};
|
|
46
|
+
(0, config_1.writeConfigData)(command.config.configDir, configData);
|
|
47
|
+
// but on OSX, we need an extra step to delete the token from the keychain
|
|
48
|
+
if (process.platform === "darwin") {
|
|
49
|
+
await (0, keychain_1.deleteTokenFromKeychain)(email);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
exports.removeOpalCredentials = removeOpalCredentials;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deleteTokenFromKeychain = exports.getTokenFromKeychain = exports.setTokenInKeychain = void 0;
|
|
4
|
+
const keychain = require("keychain");
|
|
5
|
+
const OPAL_KEYCHAIN_SERVICE = 'opal-cli';
|
|
6
|
+
// On OSX, we can easily work with the system Keychain to store the access token.
|
|
7
|
+
const setTokenInKeychain = async (email, accessToken) => {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
keychain.setPassword({ account: email, service: OPAL_KEYCHAIN_SERVICE, password: accessToken }, function (err) {
|
|
10
|
+
if (err) {
|
|
11
|
+
reject(err);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
resolve();
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
exports.setTokenInKeychain = setTokenInKeychain;
|
|
20
|
+
const getTokenFromKeychain = async (email) => {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
keychain.getPassword({ account: email, service: OPAL_KEYCHAIN_SERVICE }, function (err, password) {
|
|
23
|
+
if (err && (err === null || err === void 0 ? void 0 : err.type) !== 'PasswordNotFoundError') {
|
|
24
|
+
reject(err);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
resolve(password);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
exports.getTokenFromKeychain = getTokenFromKeychain;
|
|
33
|
+
const deleteTokenFromKeychain = async (email) => {
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
keychain.deletePassword({ service: OPAL_KEYCHAIN_SERVICE, account: email }, function () {
|
|
36
|
+
// we might get errors here if the password doesn't exist, which we want to swallow
|
|
37
|
+
resolve();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
exports.deleteTokenFromKeychain = deleteTokenFromKeychain;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export declare const setTokenInConfig: (command: Command, configData: Record<string, any>, accessToken: string) => Promise<void>;
|
|
3
|
+
export declare const getTokenFromConfig: (credsConfig: Record<string, any>) => Promise<string | undefined>;
|