From 10bc368bbc2a2137538d388ca35464b401afd0ef Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Wed, 11 Dec 2024 10:54:52 +0100 Subject: [PATCH] feat(auth, backend): seed operator tenant (#3156) * feat(auth): migration to seed operator tenant * feat(backend): migration to seed operator tenant * chore(localenv): add env vars for operator tenant * test(backend): set operator env variables in jest config * test(auth): set operator env variables in jest config * test(auth, backend): load env vars into jest environment script * feat(auth,backend): update migrations with error messages * test(integration): adding operator tenant vars * chore(backend, localenv): replace OPERATOR_TENANT_SECRET with existing API_SECRET --- localenv/cloud-nine-wallet/docker-compose.yml | 2 + localenv/happy-life-bank/docker-compose.yml | 2 + packages/auth/jest.env.js | 1 + packages/auth/jest.setup.js | 1 + .../20241205153036_seed_operator_tenant.js | 47 +++++++++++++++++++ packages/auth/src/config/app.ts | 3 +- packages/backend/jest.config.js | 16 +------ packages/backend/jest.env.js | 16 +++++++ packages/backend/jest.setup.ts | 1 + .../20241205153035_seed_operator_tenant.js | 30 ++++++++++++ packages/backend/src/config/app.ts | 5 +- .../cloud-nine-wallet/docker-compose.yml | 3 ++ .../happy-life-bank/docker-compose.yml | 3 ++ 13 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 packages/auth/migrations/20241205153036_seed_operator_tenant.js create mode 100644 packages/backend/jest.env.js create mode 100644 packages/backend/migrations/20241205153035_seed_operator_tenant.js diff --git a/localenv/cloud-nine-wallet/docker-compose.yml b/localenv/cloud-nine-wallet/docker-compose.yml index a493be4b71..6f22a11e27 100644 --- a/localenv/cloud-nine-wallet/docker-compose.yml +++ b/localenv/cloud-nine-wallet/docker-compose.yml @@ -78,6 +78,7 @@ services: ILP_CONNECTOR_URL: ${CLOUD_NINE_CONNECTOR_URL:-http://cloud-nine-wallet-backend:3002} ENABLE_TELEMETRY: true KEY_ID: 7097F83B-CB84-469E-96C6-2141C72E22C0 + OPERATOR_TENANT_ID: 438fa74a-fa7d-4317-9ced-dde32ece1787 depends_on: - shared-database - shared-redis @@ -117,6 +118,7 @@ services: IDENTITY_SERVER_SECRET: 2pEcn2kkCclbOHQiGNEwhJ0rucATZhrA807HTm2rNXE= COOKIE_KEY: 42397d1f371dd4b8b7d0308a689a57c882effd4ea909d792302542af47e2cd37 ADMIN_API_SECRET: rPoZpe9tVyBNCigm05QDco7WLcYa0xMao7lO5KG1XG4= + OPERATOR_TENANT_ID: 438fa74a-fa7d-4317-9ced-dde32ece1787 depends_on: - shared-database - shared-redis diff --git a/localenv/happy-life-bank/docker-compose.yml b/localenv/happy-life-bank/docker-compose.yml index 93475143f1..15d41cce4a 100644 --- a/localenv/happy-life-bank/docker-compose.yml +++ b/localenv/happy-life-bank/docker-compose.yml @@ -71,6 +71,7 @@ services: WALLET_ADDRESS_URL: ${HAPPY_LIFE_BANK_WALLET_ADDRESS_URL:-https://happy-life-bank-backend/.well-known/pay} ENABLE_TELEMETRY: true KEY_ID: 53f2d913-e98a-40b9-b270-372d0547f23d + OPERATOR_TENANT_ID: cf5fd7d3-1eb1-4041-8e43-ba45747e9e5d depends_on: - cloud-nine-backend healthcheck: @@ -106,6 +107,7 @@ services: IDENTITY_SERVER_SECRET: 2pEcn2kkCclbOHQiGNEwhJ0rucATZhrA807HTm2rNXE= COOKIE_KEY: 42397d1f371dd4b8b7d0308a689a57c882effd4ea909d792302542af47e2cd37 ADMIN_API_SECRET: rPoZpe9tVyBNCigm05QDco7WLcYa0xMao7lO5KG1XG4= + OPERATOR_TENANT_ID: cf5fd7d3-1eb1-4041-8e43-ba45747e9e5d depends_on: - cloud-nine-auth happy-life-admin: diff --git a/packages/auth/jest.env.js b/packages/auth/jest.env.js index 712a6a7a31..423f55578a 100644 --- a/packages/auth/jest.env.js +++ b/packages/auth/jest.env.js @@ -4,3 +4,4 @@ process.env.IDENTITY_SERVER_SECRET = '2pEcn2kkCclbOHQiGNEwhJ0rucATZhrA807HTm2rNXE=' process.env.AUTH_SERVER_URL = 'http://localhost:3006' process.env.IDENTITY_SERVER_URL = 'http://localhost:3030/mock-idp/' +process.env.OPERATOR_TENANT_ID = 'cf5fd7d3-1eb1-4041-8e43-ba45747e9e5d' diff --git a/packages/auth/jest.setup.js b/packages/auth/jest.setup.js index edbfb6f7ec..b232ee53ad 100644 --- a/packages/auth/jest.setup.js +++ b/packages/auth/jest.setup.js @@ -2,6 +2,7 @@ const { knex } = require('knex') // eslint-disable-next-line @typescript-eslint/no-var-requires const { GenericContainer, Wait } = require('testcontainers') +require('./jest.env') // set environment variables const POSTGRES_PORT = 5432 const REDIS_PORT = 6379 diff --git a/packages/auth/migrations/20241205153036_seed_operator_tenant.js b/packages/auth/migrations/20241205153036_seed_operator_tenant.js new file mode 100644 index 0000000000..a7288e1ccf --- /dev/null +++ b/packages/auth/migrations/20241205153036_seed_operator_tenant.js @@ -0,0 +1,47 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ + +const OPERATOR_TENANT_ID = process.env['OPERATOR_TENANT_ID'] +const IDENTITY_SERVER_URL = process.env['IDENTITY_SERVER_URL'] +const IDENTITY_SERVER_SECRET = process.env['IDENTITY_SERVER_SECRET'] + +exports.up = function (knex) { + if (!OPERATOR_TENANT_ID) { + throw new Error( + 'Could not seed operator tenant. Please configure OPERATOR_TENANT_ID environment variables' + ) + } + + const seed = { + id: OPERATOR_TENANT_ID + } + + if (IDENTITY_SERVER_URL) { + seed['idpConsentUrl'] = IDENTITY_SERVER_URL + } + + if (IDENTITY_SERVER_SECRET) { + seed['idpSecret'] = IDENTITY_SERVER_SECRET + } + + return knex.raw(` + INSERT INTO "tenants" (${Object.keys(seed) + .map((key) => `"${key}"`) + .join(', ')}) + VALUES (${Object.values(seed) + .map((key) => `'${key}'`) + .join(', ')}) + `) +} + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.raw(` + TRUNCATE "tenants" + `) +} diff --git a/packages/auth/src/config/app.ts b/packages/auth/src/config/app.ts index 549d71cb9a..f3372e16f6 100644 --- a/packages/auth/src/config/app.ts +++ b/packages/auth/src/config/app.ts @@ -78,7 +78,8 @@ export const Config = { process.env.REDIS_TLS_CA_FILE_PATH, process.env.REDIS_TLS_KEY_FILE_PATH, process.env.REDIS_TLS_CERT_FILE_PATH - ) + ), + operatorTenantId: envString('OPERATOR_TENANT_ID') } function parseRedisTlsConfig( diff --git a/packages/backend/jest.config.js b/packages/backend/jest.config.js index c0245a4d3b..cdb688ff95 100644 --- a/packages/backend/jest.config.js +++ b/packages/backend/jest.config.js @@ -4,26 +4,12 @@ const baseConfig = require('../../jest.config.base.js') // eslint-disable-next-line @typescript-eslint/no-var-requires const packageName = require('./package.json').name -process.env.LOG_LEVEL = 'silent' -process.env.INSTANCE_NAME = 'Rafiki' -process.env.KEY_ID = 'myKey' -process.env.OPEN_PAYMENTS_URL = 'http://127.0.0.1:3000' -process.env.ILP_CONNECTOR_URL = 'http://127.0.0.1:3002' -process.env.ILP_ADDRESS = 'test.rafiki' -process.env.AUTH_SERVER_GRANT_URL = 'http://127.0.0.1:3006' -process.env.AUTH_SERVER_INTROSPECTION_URL = 'http://127.0.0.1:3007/' -process.env.AUTH_ADMIN_API_URL = 'http://127.0.0.1:3003/graphql' -process.env.AUTH_ADMIN_API_SECRET = 'test-secret' -process.env.WEBHOOK_URL = 'http://127.0.0.1:4001/webhook' -process.env.STREAM_SECRET = '2/PxuRFV9PAp0yJlnAifJ+1OxujjjI16lN+DBnLNRLA=' -process.env.USE_TIGERBEETLE = false -process.env.ENABLE_TELEMETRY = false - module.exports = { ...baseConfig, clearMocks: true, testTimeout: 30000, roots: [`/packages/${packageName}`], + setupFiles: [`/packages/${packageName}/jest.env.js`], globalSetup: `/packages/${packageName}/jest.setup.ts`, globalTeardown: `/packages/${packageName}/jest.teardown.js`, testRegex: `(packages/${packageName}/.*/__tests__/.*|\\.(test|spec))\\.tsx?$`, diff --git a/packages/backend/jest.env.js b/packages/backend/jest.env.js new file mode 100644 index 0000000000..5509b4f583 --- /dev/null +++ b/packages/backend/jest.env.js @@ -0,0 +1,16 @@ +process.env.LOG_LEVEL = 'silent' +process.env.INSTANCE_NAME = 'Rafiki' +process.env.KEY_ID = 'myKey' +process.env.OPEN_PAYMENTS_URL = 'http://127.0.0.1:3000' +process.env.ILP_CONNECTOR_URL = 'http://127.0.0.1:3002' +process.env.ILP_ADDRESS = 'test.rafiki' +process.env.AUTH_SERVER_GRANT_URL = 'http://127.0.0.1:3006' +process.env.AUTH_SERVER_INTROSPECTION_URL = 'http://127.0.0.1:3007/' +process.env.WEBHOOK_URL = 'http://127.0.0.1:4001/webhook' +process.env.STREAM_SECRET = '2/PxuRFV9PAp0yJlnAifJ+1OxujjjI16lN+DBnLNRLA=' +process.env.USE_TIGERBEETLE = false +process.env.ENABLE_TELEMETRY = false +process.env.AUTH_ADMIN_API_URL = 'http://127.0.0.1:3003/graphql' +process.env.AUTH_ADMIN_API_SECRET = 'test-secret' +process.env.OPERATOR_TENANT_ID = 'cf5fd7d3-1eb1-4041-8e43-ba45747e9e5d' +process.env.API_SECRET = 'KQEXlZO65jUJXakXnLxGO7dk387mt71G9tZ42rULSNU=' diff --git a/packages/backend/jest.setup.ts b/packages/backend/jest.setup.ts index ff90fa720f..ef4340581d 100644 --- a/packages/backend/jest.setup.ts +++ b/packages/backend/jest.setup.ts @@ -1,5 +1,6 @@ import { knex } from 'knex' import { GenericContainer, Wait } from 'testcontainers' +require('./jest.env') // set environment variables const POSTGRES_PORT = 5432 const REDIS_PORT = 6379 diff --git a/packages/backend/migrations/20241205153035_seed_operator_tenant.js b/packages/backend/migrations/20241205153035_seed_operator_tenant.js new file mode 100644 index 0000000000..6af21658c1 --- /dev/null +++ b/packages/backend/migrations/20241205153035_seed_operator_tenant.js @@ -0,0 +1,30 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ + +const OPERATOR_TENANT_ID = process.env['OPERATOR_TENANT_ID'] +const OPERATOR_API_SECRET = process.env['API_SECRET'] + +exports.up = function (knex) { + if (!OPERATOR_TENANT_ID || !OPERATOR_API_SECRET) { + throw new Error( + 'Could not seed operator tenant. Please configure OPERATOR_TENANT_ID and API_SECRET environment variables' + ) + } + + return knex.raw(` + INSERT INTO "tenants" ("id", "apiSecret") + VALUES ('${OPERATOR_TENANT_ID}', '${OPERATOR_API_SECRET}') + `) +} + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.raw(` + TRUNCATE "tenants" + `) +} diff --git a/packages/backend/src/config/app.ts b/packages/backend/src/config/app.ts index 2f70ab2164..ec70dbb608 100644 --- a/packages/backend/src/config/app.ts +++ b/packages/backend/src/config/app.ts @@ -162,7 +162,7 @@ export const Config = { signatureSecret: process.env.SIGNATURE_SECRET, // optional signatureVersion: envInt('SIGNATURE_VERSION', 1), - adminApiSecret: process.env.API_SECRET, // optional + adminApiSecret: envString('API_SECRET'), adminApiSignatureVersion: envInt('API_SIGNATURE_VERSION', 1), adminApiSignatureTtl: envInt('ADMIN_API_SIGNATURE_TTL_SECONDS', 30), @@ -195,7 +195,8 @@ export const Config = { 'MAX_OUTGOING_PAYMENT_RETRY_ATTEMPTS', 5 ), - localCacheDuration: envInt('LOCAL_CACHE_DURATION_MS', 15_000) + localCacheDuration: envInt('LOCAL_CACHE_DURATION_MS', 15_000), + operatorTenantId: envString('OPERATOR_TENANT_ID') } function parseRedisTlsConfig( diff --git a/test/integration/testenv/cloud-nine-wallet/docker-compose.yml b/test/integration/testenv/cloud-nine-wallet/docker-compose.yml index e205c052cc..02bdc3d156 100644 --- a/test/integration/testenv/cloud-nine-wallet/docker-compose.yml +++ b/test/integration/testenv/cloud-nine-wallet/docker-compose.yml @@ -40,6 +40,8 @@ services: EXCHANGE_RATES_URL: http://host.docker.internal:8888/rates REDIS_URL: redis://shared-redis:6379/0 USE_TIGERBEETLE: false + OPERATOR_TENANT_ID: 438fa74a-fa7d-4317-9ced-dde32ece1787 + API_SECRET: iyIgCprjb9uL8wFckR+pLEkJWMB7FJhgkvqhTQR/964= volumes: - ../private-key.pem:/workspace/private-key.pem depends_on: @@ -72,6 +74,7 @@ services: IDENTITY_SERVER_URL: http://localhost:3030/mock-idp/ IDENTITY_SERVER_SECRET: 2pEcn2kkCclbOHQiGNEwhJ0rucATZhrA807HTm2rNXE= COOKIE_KEY: 42397d1f371dd4b8b7d0308a689a57c882effd4ea909d792302542af47e2cd37 + OPERATOR_TENANT_ID: 438fa74a-fa7d-4317-9ced-dde32ece1787 depends_on: - shared-database - shared-redis \ No newline at end of file diff --git a/test/integration/testenv/happy-life-bank/docker-compose.yml b/test/integration/testenv/happy-life-bank/docker-compose.yml index 6fcb2e6f39..40fbc64263 100644 --- a/test/integration/testenv/happy-life-bank/docker-compose.yml +++ b/test/integration/testenv/happy-life-bank/docker-compose.yml @@ -38,6 +38,8 @@ services: EXCHANGE_RATES_URL: http://host.docker.internal:8889/rates REDIS_URL: redis://shared-redis:6379/2 USE_TIGERBEETLE: false + OPERATOR_TENANT_ID: cf5fd7d3-1eb1-4041-8e43-ba45747e9e5d + API_SECRET: iyIgCprjb9uL8wFckR+pLEkJWMB7FJhgkvqhTQR/964= volumes: - ../private-key.pem:/workspace/private-key.pem depends_on: @@ -73,5 +75,6 @@ services: IDENTITY_SERVER_SECRET: 2pEcn2kkCclbOHQiGNEwhJ0rucATZhrA807HTm2rNXE= COOKIE_KEY: 42397d1f371dd4b8b7d0308a689a57c882effd4ea909d792302542af47e2cd37 AUTH_CHOICE_PORT: 4109 + OPERATOR_TENANT_ID: cf5fd7d3-1eb1-4041-8e43-ba45747e9e5d depends_on: - cloud-nine-wallet-test-auth \ No newline at end of file