@apolitical/server 4.1.0 → 4.2.0-bht.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apolitical/server",
3
- "version": "4.1.0",
3
+ "version": "4.2.0-bht.0",
4
4
  "description": "Node.js module to encapsulate Apolitical's express server setup",
5
5
  "author": "Apolitical Group Limited <engineering@apolitical.co>",
6
6
  "license": "MIT",
@@ -23,9 +23,9 @@
23
23
  "Node Modules"
24
24
  ],
25
25
  "dependencies": {
26
- "@apolitical/logger": "2.1.0",
27
- "@google-cloud/secret-manager": "5.6.0",
26
+ "@apolitical/logger": "3.0.0-bht.0",
28
27
  "@cloudnative/health-connect": "2.1.0",
28
+ "@google-cloud/secret-manager": "6.1.1",
29
29
  "@opentelemetry/api": "1.9.0",
30
30
  "@opentelemetry/auto-instrumentations-node": "0.57.1",
31
31
  "@opentelemetry/exporter-trace-otlp-grpc": "0.200.0",
@@ -40,7 +40,7 @@
40
40
  "cookie-parser": "1.4.6",
41
41
  "cors": "2.8.5",
42
42
  "dotenv": "16.0.3",
43
- "express": "4.18.2",
43
+ "express": "4.22.0",
44
44
  "express-jwt": "8.3.0",
45
45
  "http-status-codes": "2.2.0",
46
46
  "http-terminator": "3.2.0",
package/src/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { NODE_ENV, LOG_LEVEL } = process.env;
3
+ const { NODE_ENV, LOG_LEVEL, GOOGLE_CLOUD_PROJECT, GOOGLE_IMPERSONATE_SERVICE_ACCOUNT } = process.env;
4
4
 
5
5
  const NAME = 'apolitical-server';
6
6
  const UUID = '00000000-0000-0000-0000-000000000000';
@@ -10,6 +10,8 @@ const ADMIN_ROLE = 'administrator';
10
10
  module.exports = {
11
11
  NODE_ENV,
12
12
  LOG_LEVEL,
13
+ GOOGLE_CLOUD_PROJECT,
14
+ GOOGLE_IMPERSONATE_SERVICE_ACCOUNT,
13
15
  LOGGER_OPTIONS: {
14
16
  logLevel: LOG_LEVEL,
15
17
  labels: {
package/src/container.js CHANGED
@@ -22,6 +22,7 @@ const prerender = require('prerender-node');
22
22
  const qs = require('qs');
23
23
  const swaggerUi = require('swagger-ui-express');
24
24
  const xss = require('xss');
25
+ const secretManager = require('@google-cloud/secret-manager');
25
26
  // Internal Modules
26
27
  const apoliticalLogger = require('@apolitical/logger');
27
28
  // Configuration
@@ -36,7 +37,6 @@ const jwtEncodeHelper = require('./helpers/jwt/encode.helper');
36
37
  const jwtPassportHelper = require('./helpers/jwt/passport.helper');
37
38
  const loggerHelper = require('./helpers/logger.helper');
38
39
  const requestHelper = require('./helpers/request.helper');
39
- const impersonationHelper = require('./helpers/impersonation.helper');
40
40
  // Loaders
41
41
  const documentationLoader = require('./loaders/documentation.loader');
42
42
  const expressLoader = require('./loaders/express.loader');
@@ -82,6 +82,7 @@ container.register({
82
82
  qs: asValue(qs),
83
83
  swaggerUi: asValue(swaggerUi),
84
84
  xss: asValue(xss),
85
+ secretManager: asValue(secretManager),
85
86
  // Internal Modules
86
87
  apoliticalLogger: asValue(apoliticalLogger),
87
88
  // Configuration
@@ -96,7 +97,6 @@ container.register({
96
97
  jwtPassportHelper: asFunction(jwtPassportHelper).singleton(),
97
98
  loggerHelper: asFunction(loggerHelper).singleton(),
98
99
  requestHelper: asFunction(requestHelper).singleton(),
99
- impersonationHelper: asFunction(impersonationHelper).singleton(),
100
100
  // Loaders
101
101
  documentationLoader: asFunction(documentationLoader).singleton(),
102
102
  expressLoader: asFunction(expressLoader).singleton(),
@@ -1,57 +1,32 @@
1
1
  'use strict';
2
2
 
3
- const { SecretManagerServiceClient } = require('@google-cloud/secret-manager');
3
+ module.exports = ({ secretManager, config, logger }) => {
4
+ let client = null;
4
5
 
5
- const DEV_DEFAULTS = {
6
- projectId: 'hazel-tea-194609',
7
- impersonateAccount: 'development-platform@hazel-tea-194609.iam.gserviceaccount.com',
8
- };
6
+ const resolvedImpersonationTarget = config.GOOGLE_IMPERSONATE_SERVICE_ACCOUNT?.trim() || null;
9
7
 
10
- /**
11
- * GCP Secret Manager service for loading secrets at runtime.
12
- * Uses Application Default Credentials (ADC) with service account impersonation
13
- * for local development.
14
- */
15
- module.exports = ({ impersonationHelper, logger }) => {
16
- const cache = {};
17
- let client = null;
8
+ function isImpersonationEnabled() {
9
+ return resolvedImpersonationTarget !== null;
10
+ }
11
+
12
+ function impersonationLogContext() {
13
+ return resolvedImpersonationTarget ? { targetServiceAccount: resolvedImpersonationTarget } : {};
14
+ }
18
15
 
19
- /**
20
- * Get or create the Secret Manager client.
21
- * In non-production, auto-configures impersonation if not already set.
22
- * @returns {SecretManagerServiceClient}
23
- */
24
16
  function getClient() {
25
17
  if (!client) {
26
- if (process.env['NODE_ENV'] !== 'production' && !impersonationHelper.isImpersonationEnabled()) {
27
- process.env['GOOGLE_IMPERSONATE_SERVICE_ACCOUNT'] = DEV_DEFAULTS.impersonateAccount;
28
- logger.info('[secrets] set default impersonation for development', {
29
- targetServiceAccount: DEV_DEFAULTS.impersonateAccount,
30
- });
31
- }
32
-
33
- if (impersonationHelper.isImpersonationEnabled()) {
34
- logger.info('[secrets] using impersonation', impersonationHelper.impersonationLogContext());
18
+ if (isImpersonationEnabled()) {
19
+ logger.info('[secrets] using impersonation', impersonationLogContext());
35
20
  }
36
21
 
37
- client = new SecretManagerServiceClient();
22
+ client = new secretManager.SecretManagerServiceClient();
38
23
  }
39
24
  return client;
40
25
  }
41
26
 
42
- /**
43
- * Load secrets from Google Secret Manager and set them as environment variables.
44
- * @param {Object} opts
45
- * @param {string} [opts.projectId] - GCP project ID (defaults to DEV_DEFAULTS in non-production)
46
- * @param {Array<{name: string, envVar: string, version?: string}>} opts.secrets - Secrets to load
47
- * @throws {Error} If projectId is missing or secret cannot be loaded
48
- */
49
27
  async function loadSecrets(opts) {
50
28
  const { secrets } = opts;
51
- const projectId =
52
- opts.projectId ??
53
- process.env['GOOGLE_CLOUD_PROJECT'] ??
54
- (process.env['NODE_ENV'] !== 'production' ? DEV_DEFAULTS.projectId : undefined);
29
+ const projectId = opts.projectId ?? config.GOOGLE_CLOUD_PROJECT;
55
30
 
56
31
  if (!projectId) {
57
32
  throw new Error('Missing projectId for Secret Manager');
@@ -74,7 +49,6 @@ module.exports = ({ impersonationHelper, logger }) => {
74
49
  throw new Error(`Empty payload for secret ${spec.name}`);
75
50
  }
76
51
 
77
- cache[spec.envVar] = payload;
78
52
  process.env[spec.envVar] = payload;
79
53
 
80
54
  logger.info('[secrets] loaded', {
@@ -88,14 +62,8 @@ module.exports = ({ impersonationHelper, logger }) => {
88
62
  }
89
63
  }
90
64
 
91
- /**
92
- * Get a secret value from cache or environment.
93
- * @param {string} envVar - The environment variable name
94
- * @returns {string} The secret value
95
- * @throws {Error} If secret is not loaded
96
- */
97
65
  function getSecret(envVar) {
98
- const val = cache[envVar] ?? process.env[envVar];
66
+ const val = process.env[envVar];
99
67
  if (!val) {
100
68
  throw new Error(`Secret "${envVar}" not loaded`);
101
69
  }
@@ -105,6 +73,5 @@ module.exports = ({ impersonationHelper, logger }) => {
105
73
  return {
106
74
  loadSecrets,
107
75
  getSecret,
108
- impersonation: impersonationHelper,
109
76
  };
110
77
  };
@@ -1,42 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Helpers for Application Default Credentials (ADC) impersonation detection.
5
- *
6
- * These utilities do not acquire credentials; they only surface intent
7
- * based on environment variables so applications can log and enforce policy.
8
- */
9
-
10
- /**
11
- * Returns the target service account to impersonate if configured via env.
12
- * GOOGLE_IMPERSONATE_SERVICE_ACCOUNT is respected by Google client libraries
13
- * when acquiring credentials via ADC.
14
- * @returns {string|null}
15
- */
16
- function getImpersonationTarget() {
17
- const target = process.env['GOOGLE_IMPERSONATE_SERVICE_ACCOUNT'];
18
- return target && target.trim().length > 0 ? target.trim() : null;
19
- }
20
-
21
- /**
22
- * Whether impersonation is enabled for the current process.
23
- * @returns {boolean}
24
- */
25
- function isImpersonationEnabled() {
26
- return getImpersonationTarget() !== null;
27
- }
28
-
29
- /**
30
- * Produce a safe log payload for audit visibility.
31
- * @returns {{ targetServiceAccount: string } | {}}
32
- */
33
- function impersonationLogContext() {
34
- const target = getImpersonationTarget();
35
- return target ? { targetServiceAccount: target } : {};
36
- }
37
-
38
- module.exports = () => ({
39
- getImpersonationTarget,
40
- isImpersonationEnabled,
41
- impersonationLogContext,
42
- });