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.
Files changed (65) hide show
  1. package/README.md +233 -157
  2. package/bin/dev +6 -0
  3. package/bin/dev.cmd +3 -0
  4. package/bin/run +4 -4
  5. package/lib/commands/aws/identity.d.ts +2 -2
  6. package/lib/commands/aws/identity.js +5 -5
  7. package/lib/commands/curl-example.d.ts +2 -2
  8. package/lib/commands/curl-example.js +7 -6
  9. package/lib/commands/iam-roles/start.d.ts +6 -6
  10. package/lib/commands/iam-roles/start.js +19 -16
  11. package/lib/commands/kube-roles/start.d.ts +6 -6
  12. package/lib/commands/kube-roles/start.js +16 -13
  13. package/lib/commands/login.d.ts +4 -4
  14. package/lib/commands/login.js +28 -27
  15. package/lib/commands/logout.d.ts +3 -3
  16. package/lib/commands/logout.js +5 -5
  17. package/lib/commands/migrate-creds.d.ts +8 -0
  18. package/lib/commands/migrate-creds.js +48 -0
  19. package/lib/commands/postgres-instances/start.d.ts +7 -7
  20. package/lib/commands/postgres-instances/start.js +17 -14
  21. package/lib/commands/resources/get.d.ts +3 -3
  22. package/lib/commands/resources/get.js +8 -8
  23. package/lib/commands/set-custom-header.d.ts +4 -4
  24. package/lib/commands/set-custom-header.js +9 -9
  25. package/lib/commands/set-token.d.ts +3 -3
  26. package/lib/commands/set-token.js +10 -17
  27. package/lib/commands/set-url.d.ts +10 -12
  28. package/lib/commands/set-url.js +19 -20
  29. package/lib/commands/ssh/copyFrom.d.ts +7 -7
  30. package/lib/commands/ssh/copyFrom.js +16 -13
  31. package/lib/commands/ssh/copyTo.d.ts +7 -7
  32. package/lib/commands/ssh/copyTo.js +16 -13
  33. package/lib/commands/ssh/start.d.ts +5 -5
  34. package/lib/commands/ssh/start.js +19 -16
  35. package/lib/handler.d.ts +6 -5
  36. package/lib/handler.js +8 -6
  37. package/lib/index.d.ts +1 -1
  38. package/lib/index.js +3 -2
  39. package/lib/lib/apollo.d.ts +1 -1
  40. package/lib/lib/apollo.js +26 -19
  41. package/lib/lib/aws.js +10 -11
  42. package/lib/lib/cmd.d.ts +5 -4
  43. package/lib/lib/cmd.js +10 -5
  44. package/lib/lib/config.js +8 -5
  45. package/lib/lib/credentials/index.d.ts +11 -0
  46. package/lib/lib/credentials/index.js +52 -0
  47. package/lib/lib/credentials/keychain.d.ts +3 -0
  48. package/lib/lib/credentials/keychain.js +41 -0
  49. package/lib/lib/credentials/localEncryption.d.ts +3 -0
  50. package/lib/lib/credentials/localEncryption.js +75 -0
  51. package/lib/lib/flags.d.ts +5 -6
  52. package/lib/lib/flags.js +6 -6
  53. package/lib/lib/resources.d.ts +4 -4
  54. package/lib/lib/resources.js +14 -11
  55. package/lib/lib/sessions.d.ts +2 -2
  56. package/lib/lib/sessions.js +13 -11
  57. package/lib/lib/ssh.d.ts +1 -1
  58. package/lib/lib/ssh.js +6 -4
  59. package/lib/lib/util.js +8 -5
  60. package/lib/types.d.ts +1204 -1204
  61. package/lib/types.js +49 -49
  62. package/oclif.manifest.json +756 -1
  63. package/package.json +29 -24
  64. package/lib/lib/credentials.d.ts +0 -9
  65. package/lib/lib/credentials.js +0 -76
@@ -1,13 +1,13 @@
1
- import { Command } from '@oclif/command';
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/parser/lib/flags").IBooleanFlag<void>;
8
- id: import("@oclif/command/lib/flags").IOptionFlag<string | undefined>;
9
- sessionId: import("@oclif/command/lib/flags").IOptionFlag<string | undefined>;
10
- refresh: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
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 command_1 = require("@oclif/command");
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 command_1.Command {
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/command';
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, Record<string, any>, Record<string, any>> | null;
9
- error: any;
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: any;
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
- exports.runMutation = async ({ command, query, variables, }) => {
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.runQuery = async ({ command, query, variables, }) => {
24
- await apollo_1.initClient(command);
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/command';
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
- var command_1 = require("@oclif/command");
4
- Object.defineProperty(exports, "run", { enumerable: true, get: function () { return command_1.run; } });
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; } });
@@ -1,5 +1,5 @@
1
1
  import { ApolloClient, NormalizedCacheObject } from '@apollo/client/core';
2
- import { Command } from '@oclif/command';
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
- exports.printResponse = (command, resp) => {
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.handleError = (command, err, resp) => {
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.initClient = async (command) => {
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 accessToken = await credentials_1.cred.accessToken;
58
- const organizationID = await credentials_1.cred.organizationID;
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, _b;
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 errors = (_a = networkError === null || networkError === void 0 ? void 0 : networkError.result) === null || _a === void 0 ? void 0 : _a.errors;
143
- if (errors && errors.length > 0) {
144
- errorMessage = (_b = errors[0]) === null || _b === void 0 ? void 0 : _b.message;
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
- // remove non standard characters
10
- let sanitizedStr = str.replace(/[^A-Za-z0-9- ]/g, '');
11
- // de-dupe spaces
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
- // map remaining spaces to '-;
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
- exports.getAwsConfigUpdateCmd = (itemName, awsAccessKeyId, awsSecretAccessKey, awsSessionToken) => {
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.getAwsEnvVarMessage = () => {
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/command';
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 | undefined) => void;
9
- export declare const runCommandExec: (runCmd: string, successMessage: string, errorMessage: string, envVars?: Record<string, any> | undefined) => void;
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> | undefined) => void;
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
- exports.setMostRecentCommand = (cmd) => {
9
+ const setMostRecentCommand = (cmd) => {
10
10
  exports.mostRecentCommand = cmd;
11
11
  exports.mostRecentCommandTime = moment(new Date());
12
12
  };
13
- exports.startInteractiveShell = (runCmd, shellName) => {
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.runCommandExec = (runCmd, successMessage, errorMessage, envVars) => {
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.runCommandExecWithCallback = (cmd, callback) => {
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.runCommandSpawn = (runCmd, successMessage, errorMessage, envVars) => {
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
- exports.getOrCreateConfigData = (configDir) => {
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.writeConfigData = (configDir, newConfigData) => {
35
- const existingData = exports.getOrCreateConfigData(configDir);
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.isProduction = (configDir) => {
45
- const configData = exports.getOrCreateConfigData(configDir);
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,3 @@
1
+ export declare const setTokenInKeychain: (email: string, accessToken: string) => Promise<void>;
2
+ export declare const getTokenFromKeychain: (email: string) => Promise<string>;
3
+ export declare const deleteTokenFromKeychain: (email: string) => Promise<void>;
@@ -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>;