@govuk-pay/cli 0.0.49 → 0.0.51

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@govuk-pay/cli",
3
- "version": "0.0.49",
3
+ "version": "0.0.51",
4
4
  "description": "GOV.UK Pay Command Line Interface",
5
5
  "bin": {
6
6
  "pay": "bin/cli.js",
@@ -14,10 +14,11 @@
14
14
  "node": ">= 18.x"
15
15
  },
16
16
  "dependencies": {
17
- "@aws-sdk/client-ec2": "^3.641.0",
18
- "@aws-sdk/client-ecs": "^3.637.0",
19
- "@aws-sdk/client-rds": "^3.637.0",
20
- "@aws-sdk/client-ssm": "^3.651.1",
17
+ "@aws-sdk/client-ec2": "^3.709.0",
18
+ "@aws-sdk/client-ecs": "^3.709.0",
19
+ "@aws-sdk/client-rds": "^3.709.0",
20
+ "@aws-sdk/client-ssm": "^3.709.0",
21
+ "@aws-sdk/client-sts": "^3.709.0",
21
22
  "handlebars": "^4.7.8",
22
23
  "openurl": "^1.1.1",
23
24
  "semver": "^7.6.3",
@@ -331,6 +331,7 @@ pay-low-pass:
331
331
  DB_SUPPORT_PASSWORD_READWRITE: aws/rds/support_readwrite_users/staging/ledger_support_readwrite # pragma: allowlist secret
332
332
  SENTRY_DSN: sentry_io/ledger_dsn
333
333
  products:
334
+ DB_PASSWORD: aws/rds/application_users/staging/products
334
335
  DB_SUPPORT_PASSWORD_READONLY: aws/rds/support_readonly_users/staging/products_support_readonly # pragma: allowlist secret
335
336
  DB_SUPPORT_PASSWORD_READWRITE: aws/rds/support_readwrite_users/staging/products_support_readwrite # pragma: allowlist secret
336
337
  SENTRY_DSN: sentry_io/products_dsn
@@ -344,6 +345,7 @@ pay-low-pass:
344
345
  # the words 'Password Store'. They are not in pay-low-pass, so for now to stop them being overwritten I'm commenting them out
345
346
  # TOKEN_API_HMAC_SECRET: ""
346
347
  publicauth:
348
+ DB_PASSWORD: aws/rds/application_users/staging/publicauth
347
349
  DB_SUPPORT_PASSWORD_READONLY: aws/rds/support_readonly_users/staging/publicauth_support_readonly # pragma: allowlist secret
348
350
  DB_SUPPORT_PASSWORD_READWRITE: aws/rds/support_readwrite_users/staging/publicauth_support_readwrite # pragma: allowlist secret
349
351
  SENTRY_DSN: sentry_io/publicauth_dsn
@@ -438,6 +440,7 @@ pay-low-pass:
438
440
  product-page:
439
441
  pager_duty_cloudwatch_integration_url: pager-duty/govuk-pay-product-page/amazon-cloudwatch-integration-url
440
442
  products:
443
+ DB_PASSWORD: aws/rds/application_users/production/products
441
444
  DB_SUPPORT_PASSWORD_READONLY: aws/rds/support_readonly_users/production/products_support_readonly # pragma: allowlist secret
442
445
  DB_SUPPORT_PASSWORD_READWRITE: aws/rds/support_readwrite_users/production/products_support_readwrite # pragma: allowlist secret
443
446
  SENTRY_DSN: sentry_io/products_dsn
@@ -449,6 +452,7 @@ pay-low-pass:
449
452
  publicapi:
450
453
  SENTRY_DSN: sentry_io/publicapi_dsn
451
454
  publicauth:
455
+ DB_PASSWORD: aws/rds/application_users/production/publicauth
452
456
  DB_SUPPORT_PASSWORD_READONLY: aws/rds/support_readonly_users/production/publicauth_support_readonly # pragma: allowlist secret
453
457
  DB_SUPPORT_PASSWORD_READWRITE: aws/rds/support_readwrite_users/production/publicauth_support_readwrite # pragma: allowlist secret
454
458
  SENTRY_DSN: sentry_io/publicauth_dsn
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.handler = exports.builder = exports.desc = exports.command = void 0;
7
7
  const standardContent_js_1 = require("../core/standardContent.js");
8
+ const configs_js_1 = require("../util/configs.js");
8
9
  const client_ec2_1 = require("@aws-sdk/client-ec2");
9
10
  const client_ecs_1 = require("@aws-sdk/client-ecs");
10
11
  const client_rds_1 = require("@aws-sdk/client-rds");
@@ -75,6 +76,7 @@ async function tunnelHandler(argv) {
75
76
  await (0, standardContent_js_1.showHeader)();
76
77
  const environment = argv.environment;
77
78
  const application = argv.application;
79
+ await (0, configs_js_1.checkAwsCredentials)(environment.split('-')[0]);
78
80
  console.log(`Opening a database tunnel to ${environment} ${application}`);
79
81
  let maximumDuration = 90;
80
82
  if (process.env.MAXIMUM_DURATION !== undefined) {
@@ -443,7 +445,7 @@ async function getDbUser(environment, application, dbAccessType) {
443
445
  }
444
446
  else {
445
447
  printError('Unable to get database username from Parameter Store');
446
- return '';
448
+ throw new Error(`The parameter store parameter ${paramName} is missing in ${environment}`);
447
449
  }
448
450
  }
449
451
  function getPayLowPassDbSecretName(environment, user, dbAccessType) {
@@ -1,33 +1,13 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
4
  };
25
5
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.APPLICATIONS = exports.ENVIRONMENTS = exports.distDirForBuildTasks = exports.rootDirForBuildTasks = exports.rootDir = void 0;
27
- const path = __importStar(require("path"));
28
- exports.rootDir = path.resolve(__dirname, '..', '..');
29
- exports.rootDirForBuildTasks = exports.rootDir.endsWith('dist') ? path.resolve(exports.rootDir, '..') : exports.rootDir;
30
- exports.distDirForBuildTasks = path.join(exports.rootDirForBuildTasks, 'dist');
6
+ exports.AWS_ACCOUNT_IDS = exports.APPLICATIONS = exports.ENVIRONMENTS = exports.distDirForBuildTasks = exports.rootDirForBuildTasks = exports.rootDir = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ exports.rootDir = path_1.default.resolve(__dirname, '..', '..');
9
+ exports.rootDirForBuildTasks = exports.rootDir.endsWith('dist') ? path_1.default.resolve(exports.rootDir, '..') : exports.rootDir;
10
+ exports.distDirForBuildTasks = path_1.default.join(exports.rootDirForBuildTasks, 'dist');
31
11
  exports.ENVIRONMENTS = ['test-12', 'test-perf-1', 'staging-2', 'deploy-tooling', 'production-2'];
32
12
  exports.APPLICATIONS = [
33
13
  'adminusers',
@@ -46,6 +26,13 @@ exports.APPLICATIONS = [
46
26
  'toolbox',
47
27
  'webhooks'
48
28
  ];
29
+ exports.AWS_ACCOUNT_IDS = {
30
+ dev: '673337093959',
31
+ test: '223851549868',
32
+ staging: '888564216586',
33
+ production: '092359438320',
34
+ deploy: '424875624006'
35
+ };
49
36
  exports.default = {
50
37
  rootDir: exports.rootDir,
51
38
  distDirForBuildTasks: exports.distDirForBuildTasks,
@@ -3,10 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.workspaceEnvVar = exports.ensureConfigDirectory = exports.PAY_CLI_CONFIG_PATH = void 0;
6
+ exports.checkAwsCredentials = exports.workspaceEnvVar = exports.ensureConfigDirectory = exports.PAY_CLI_CONFIG_PATH = void 0;
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const node_os_1 = require("node:os");
9
9
  const node_path_1 = __importDefault(require("node:path"));
10
+ const client_sts_1 = require("@aws-sdk/client-sts");
11
+ const constants_1 = require("../core/constants");
10
12
  exports.PAY_CLI_CONFIG_PATH = node_path_1.default.join((0, node_os_1.homedir)(), '.pay-cli');
11
13
  /**
12
14
  * This function validates a config path within the pay cli config dir exists, if it does then it creates it.
@@ -45,3 +47,62 @@ function workspaceEnvVar() {
45
47
  return workspacePath;
46
48
  }
47
49
  exports.workspaceEnvVar = workspaceEnvVar;
50
+ const AWS_ENV_VARS_TO_CHECK = [
51
+ 'AWS_ACCESS_KEY_ID',
52
+ 'AWS_SECRET_ACCESS_KEY',
53
+ 'AWS_SESSION_TOKEN',
54
+ 'AWS_CREDENTIAL_EXPIRATION'
55
+ ];
56
+ /**
57
+ * This function checks if there are AWS credentials in the environment, that they are for a named account, and their expiry status.
58
+ * If there are none, or are some but they are expired, it will exit the program with an Error
59
+ * If there is less than 15 mins before expiry it will print a warning
60
+ *
61
+ * The accountName is intentionally a string and not a named type in order to mean the callers of this function don't need to implement
62
+ * validation checking of something passed in at runtime
63
+ */
64
+ async function checkAwsCredentials(accountName) {
65
+ for (const envVar of AWS_ENV_VARS_TO_CHECK) {
66
+ if (process.env[envVar] === undefined || process.env[envVar] === null || process.env[envVar]?.trim() === '') {
67
+ console.error(`It looks like you do not have any AWS credentials in the environment (missing ${envVar}).`);
68
+ console.error('Perhaps you need to run this with aws-vault. E.g. `aws-vault exec test -- pay ...`');
69
+ process.exit(1);
70
+ }
71
+ }
72
+ const expirationTime = process.env.AWS_CREDENTIAL_EXPIRATION;
73
+ const expirationTimestamp = Date.parse(expirationTime);
74
+ const now = Date.now();
75
+ if (now >= expirationTimestamp) {
76
+ console.error(`There are AWS credentials in the env, but they have expired. They expired at ${expirationTime}`);
77
+ process.exit(1);
78
+ }
79
+ // 15 minutes * 60 seconds in a minute * 1000 ms in a second
80
+ if (now >= (expirationTimestamp - (15 * 60 * 1000))) {
81
+ console.warn(`WARNING: The AWS credentials will expire at ${expirationTime} which is within 15 minutes\n`);
82
+ }
83
+ await checkAwsCredentialsAreForAccount(accountName);
84
+ }
85
+ exports.checkAwsCredentials = checkAwsCredentials;
86
+ /* This function will validate that the AWS credentials in the environment belong to the named account.
87
+ *
88
+ * If they are not then it will exit with an appropriate error
89
+ */
90
+ async function checkAwsCredentialsAreForAccount(accountName) {
91
+ if (!Object.hasOwn(constants_1.AWS_ACCOUNT_IDS, accountName)) {
92
+ console.error(`AWS Account ${accountName} is unknown, perhaps you meant one of ${Object.keys(constants_1.AWS_ACCOUNT_IDS).join(',')}?`);
93
+ process.exit(1);
94
+ }
95
+ const typedAccountName = accountName;
96
+ const stsClient = new client_sts_1.STSClient();
97
+ const getCallerIdentityCommand = new client_sts_1.GetCallerIdentityCommand({});
98
+ const response = await stsClient.send(getCallerIdentityCommand);
99
+ if (response.Account === undefined) {
100
+ console.error('Error checking if AWS credentials are for the correct account, the response from AWS did not include an Account ID');
101
+ process.exit(1);
102
+ }
103
+ if (constants_1.AWS_ACCOUNT_IDS[typedAccountName] !== response.Account) {
104
+ console.error(`Error: You're trying to perform an action against AWS account '${accountName}'`);
105
+ console.error(`Your credentials are not for that account. Perhaps you need to run 'aws-vault exec ${accountName} -- ...`);
106
+ process.exit(1);
107
+ }
108
+ }