From 269c23bced0341cd38eadef64ecf868078440aea Mon Sep 17 00:00:00 2001 From: Roger Floriano <31597636+petruki@users.noreply.github.com> Date: Sun, 14 May 2023 15:55:45 -0700 Subject: [PATCH] #389 - Moved verification code to Domain level (#412) --- src/api-docs/paths/path-config.js | 28 ------------- src/api-docs/paths/path-domain.js | 30 +++++++++++++- src/api-docs/schemas/config.js | 4 -- src/api-docs/schemas/domain.js | 9 +++++ src/client/relay/index.js | 2 +- src/models/config.js | 3 -- src/models/domain.js | 5 +++ src/routers/config.js | 11 ------ src/routers/domain.js | 11 ++++++ src/services/config.js | 16 ++------ src/services/domain.js | 15 +++++++ tests/config-relay.test.js | 65 ++++++++++++++++++++++++++----- tests/relay.test.js | 1 - 13 files changed, 128 insertions(+), 72 deletions(-) diff --git a/src/api-docs/paths/path-config.js b/src/api-docs/paths/path-config.js index 4029934..d0c5b01 100644 --- a/src/api-docs/paths/path-config.js +++ b/src/api-docs/paths/path-config.js @@ -274,34 +274,6 @@ export default { } } }, - '/config/relay/verificationCode/{id}': { - patch: { - tags: ['Config'], - description: 'Config Relay generates verification code', - security: [{ bearerAuth: [] }], - parameters: [ - pathParameter('id', 'Config ID', true) - ], - responses: { - '200': { - description: 'Config Relay verification code generated', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - code: { - type: 'string', - description: 'Verification code' - } - } - } - } - } - } - } - } - }, '/config/relay/verify/{id}/{env}': { patch: { tags: ['Config'], diff --git a/src/api-docs/paths/path-domain.js b/src/api-docs/paths/path-domain.js index 071f004..f1a90c6 100644 --- a/src/api-docs/paths/path-domain.js +++ b/src/api-docs/paths/path-domain.js @@ -231,5 +231,33 @@ export default { } } } - } + }, + '/domain/relay/verificationCode/{id}': { + patch: { + tags: ['Domain'], + description: 'Generates verification code for Relay integration', + security: [{ bearerAuth: [] }], + parameters: [ + pathParameter('id', 'Domain ID', true) + ], + responses: { + '200': { + description: 'Relay verification code generated', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + code: { + type: 'string', + description: 'Verification code' + } + } + } + } + } + } + } + } + }, }; \ No newline at end of file diff --git a/src/api-docs/schemas/config.js b/src/api-docs/schemas/config.js index 627df82..dea8d27 100644 --- a/src/api-docs/schemas/config.js +++ b/src/api-docs/schemas/config.js @@ -43,10 +43,6 @@ export const relay = { type: 'string' } }, - verification_code: { - type: 'string', - description: 'Generated string used to verify Relay endpoint ownership' - }, verified: { type: 'object', additionalProperties: { diff --git a/src/api-docs/schemas/domain.js b/src/api-docs/schemas/domain.js index b98aa1a..ffcd434 100644 --- a/src/api-docs/schemas/domain.js +++ b/src/api-docs/schemas/domain.js @@ -11,6 +11,15 @@ export const domain = { slack: { type: 'string', description: 'The slack integration id' + }, + relay: { + type: 'object', + properties: { + verification_code: { + type: 'string', + description: 'Generated string used to verify Relay ownership' + } + } } } }, diff --git a/src/client/relay/index.js b/src/client/relay/index.js index e25d2c7..0a97f9b 100644 --- a/src/client/relay/index.js +++ b/src/client/relay/index.js @@ -34,7 +34,7 @@ export async function resolveVerification(relay, environment) { const endpoint = relay.endpoint.get(environment)?.replace(/\/$/, ''); const url = `${endpoint?.substring(0, endpoint.lastIndexOf('/'))}/verify`; const header = createHeader(relay.auth_prefix, relay.auth_token.get(environment)); - const response = await get(url, `?code=${relay.verification_code}`, header); + const response = await get(url, '', header); return response.data?.code; } diff --git a/src/models/config.js b/src/models/config.js index 1d6a31f..cfdd0d7 100644 --- a/src/models/config.js +++ b/src/models/config.js @@ -98,9 +98,6 @@ const configSchema = new mongoose.Schema({ type: Map, of: String }, - verification_code: { - type: String - }, verified: { type: Map, of: Boolean, diff --git a/src/models/domain.js b/src/models/domain.js index 0e253d7..7da487b 100644 --- a/src/models/domain.js +++ b/src/models/domain.js @@ -37,6 +37,11 @@ const domainSchema = new mongoose.Schema({ slack: { type: mongoose.Schema.Types.ObjectId, ref: 'Slack' + }, + relay: { + verification_code: { + type: String + } } }, lastUpdate: { diff --git a/src/routers/config.js b/src/routers/config.js index 8c96694..4d1d776 100644 --- a/src/routers/config.js +++ b/src/routers/config.js @@ -210,17 +210,6 @@ router.patch('/config/removeRelay/:id/:env', auth, [ } }); -router.patch('/config/relay/verificationCode/:id', auth, [ - check('id').isMongoId() -], validate, async (req, res) => { - try { - const config = await Services.getRelayVerificationCode(req.params.id, req.admin); - res.send({ code: config.relay.verification_code }); - } catch (e) { - responseException(res, e, 500); - } -}); - router.patch('/config/relay/verify/:id/:env', auth, [ check('id').isMongoId(), check('env').isLength({ min: 1 }) diff --git a/src/routers/domain.js b/src/routers/domain.js index 9572568..018200d 100644 --- a/src/routers/domain.js +++ b/src/routers/domain.js @@ -148,4 +148,15 @@ router.patch('/domain/removeStatus/:id', auth, [ } }); +router.patch('/domain/relay/verificationCode/:id', auth, [ + check('id').isMongoId() +], validate, async (req, res) => { + try { + const code = await Services.getRelayVerificationCode(req.params.id, req.admin); + res.send({ code }); + } catch (e) { + responseException(res, e, 500); + } +}); + export default router; \ No newline at end of file diff --git a/src/services/config.js b/src/services/config.js index 021f3cd..3e74966 100644 --- a/src/services/config.js +++ b/src/services/config.js @@ -1,10 +1,9 @@ import mongoose from 'mongoose'; -import { randomUUID } from 'crypto'; import { response } from './common'; import { Config } from '../models/config'; import { formatInput, verifyOwnership, checkEnvironmentStatusRemoval } from '../helpers'; import { ActionTypes, RouterTypes } from '../models/permission'; -import { updateDomainVersion } from './domain'; +import { getDomainById, updateDomainVersion } from './domain'; import { getGroupConfigById } from './group-config'; import { checkSwitcher } from '../external/switcher-api-facade'; import { BadRequestError, NotFoundError } from '../exceptions'; @@ -264,22 +263,13 @@ export async function removeRelay(id, env, admin) { return config; } -export async function getRelayVerificationCode(id, admin) { - let config = await getConfigById(id); - config = await verifyOwnership(admin, config, config.domain, ActionTypes.UPDATE, RouterTypes.CONFIG); - - config.updatedBy = admin.email; - config.relay.verification_code = randomUUID(); - - return config.save(); -} - export async function verifyRelay(id, env, admin) { let config = await getConfigById(id); + let domain = await getDomainById(config.domain); config = await verifyOwnership(admin, config, config.domain, ActionTypes.UPDATE, RouterTypes.CONFIG); const code = await resolveVerification(config.relay, env); - if (!config.relay.verified?.get(env) && Object.is(config.relay.verification_code, code)) { + if (!config.relay.verified?.get(env) && Object.is(domain.integrations.relay.verification_code, code)) { config.relay.verified.set(env, true); await config.save(); return 'verified'; diff --git a/src/services/domain.js b/src/services/domain.js index 887ec5c..2bf2cd1 100644 --- a/src/services/domain.js +++ b/src/services/domain.js @@ -1,3 +1,4 @@ +import { randomUUID } from 'crypto'; import { checkEnvironmentStatusChange_v2 } from '../middleware/validators'; import Component from '../models/component'; import { Config } from '../models/config'; @@ -133,4 +134,18 @@ export async function updateDomainVersion(domainId) { const domain = await getDomainById(domainId); domain.lastUpdate = Date.now(); domain.save(); +} + +export async function getRelayVerificationCode(id, admin) { + let domain = await getDomainById(id); + domain = await verifyOwnership(admin, domain, domain._id, ActionTypes.UPDATE, RouterTypes.DOMAIN); + + domain.updatedBy = admin.email; + + if (!domain.integrations.relay.verification_code) { + domain.integrations.relay.verification_code = randomUUID(); + await domain.save(); + } + + return domain.integrations.relay.verification_code; } \ No newline at end of file diff --git a/tests/config-relay.test.js b/tests/config-relay.test.js index 6a0d8df..bf8e863 100644 --- a/tests/config-relay.test.js +++ b/tests/config-relay.test.js @@ -9,6 +9,7 @@ import { adminMasterAccountToken, domainId, configId1, + configId2, } from './fixtures/db_api'; import { EnvType } from '../src/models/environment'; @@ -87,7 +88,6 @@ describe('Testing relay verification', () => { // Config has a verified Relay let config = await Config.findById(configId1).exec(); config.relay.verified.set(EnvType.DEFAULT, true); - config.relay.verification_code = '123'; await config.save(); expect(config.relay.verified.get(EnvType.DEFAULT)).toBe(true); @@ -102,7 +102,6 @@ describe('Testing relay verification', () => { config = await Config.findById(configId1).exec(); expect(config.relay.verified.get(EnvType.DEFAULT)).toBe(false); - expect(config.relay.verification_code).toBe('123'); }); test('CONFIG_RELAY_SUITE - Should NOT reset Relay verified flag when changing anything but endpoint', async () => { @@ -119,7 +118,6 @@ describe('Testing relay verification', () => { // Config has a verified Relay let config = await Config.findById(configId1).exec(); config.relay.verified.set(EnvType.DEFAULT, true); - config.relay.verification_code = '123'; await config.save(); expect(config.relay.verified.get(EnvType.DEFAULT)).toBe(true); @@ -134,7 +132,6 @@ describe('Testing relay verification', () => { config = await Config.findById(configId1).exec(); expect(config.relay.verified.get(EnvType.DEFAULT)).toBe(true); - expect(config.relay.verification_code).toBe('123'); }); }); @@ -167,7 +164,6 @@ describe('Testing relay association', () => { // DB validation - document updated const config = await Config.findById(configId1).lean().exec(); expect(config.relay.verified[EnvType.DEFAULT]).toEqual(false); - expect(config.relay.verification_code).toEqual(undefined); expect(config.relay.activated[EnvType.DEFAULT]).toEqual(true); expect(config.relay.endpoint[EnvType.DEFAULT]).toBe('http://localhost:3001'); expect(config.relay.auth_token[EnvType.DEFAULT]).toEqual('123'); @@ -365,7 +361,7 @@ describe('Testing relay association', () => { test('CONFIG_RELAY_SUITE - Should generate verification code', async () => { const response = await request(app) - .patch(`/config/relay/verificationCode/${configId1}`) + .patch(`/domain/relay/verificationCode/${domainId}`) .set('Authorization', `Bearer ${adminMasterAccountToken}`) .send().expect(200); @@ -374,14 +370,14 @@ describe('Testing relay association', () => { test('CONFIG_RELAY_SUITE - Should NOT generate verification code - Config not found', async () => { await request(app) - .patch(`/config/relay/verificationCode/${new mongoose.Types.ObjectId()}`) + .patch(`/domain/relay/verificationCode/${new mongoose.Types.ObjectId()}`) .set('Authorization', `Bearer ${adminMasterAccountToken}`) .send(bodyRelayProd).expect(404); }); test('CONFIG_RELAY_SUITE - Should verify code', async () => { // Given - // Adding relay + // Adding Relay await request(app) .patch(`/config/updateRelay/${configId1}`) .set('Authorization', `Bearer ${adminMasterAccountToken}`) @@ -394,7 +390,7 @@ describe('Testing relay association', () => { // Request verification code let response = await request(app) - .patch(`/config/relay/verificationCode/${configId1}`) + .patch(`/domain/relay/verificationCode/${domainId}`) .set('Authorization', `Bearer ${adminMasterAccountToken}`) .send().expect(200); @@ -417,7 +413,7 @@ describe('Testing relay association', () => { // Given // Request verification code await request(app) - .patch(`/config/relay/verificationCode/${configId1}`) + .patch(`/domain/relay/verificationCode/${domainId}`) .set('Authorization', `Bearer ${adminMasterAccountToken}`) .send().expect(200); @@ -455,4 +451,53 @@ describe('Testing relay association', () => { expect(response.body.status).toBe('failed'); }); +}); + +describe('Testing relay verification - Existing verified Relays', () => { + beforeAll(async () => { + await setupDatabase(); + }); + + const bodyRelay = { + type: 'VALIDATION', + activated: { default: true }, + endpoint: { default: {} }, + method: 'POST' + }; + + test('CONFIG_RELAY_SUITE - Should NOT generate a new verification code - Existing Relay has it', async () => { + // Given + // Add Relay 1 + bodyRelay.endpoint.default = 'https://localhost:3001/validate'; + await request(app) + .patch(`/config/updateRelay/${configId1}`) + .set('Authorization', `Bearer ${adminMasterAccountToken}`) + .send(bodyRelay).expect(200); + + // Request verification code for Relay 1 + let response = await request(app) + .patch(`/domain/relay/verificationCode/${domainId}`) + .set('Authorization', `Bearer ${adminMasterAccountToken}`) + .send().expect(200); + + const code1 = response.body.code; + expect(code1).not.toBe(undefined); + + // Add Relay 2 + bodyRelay.endpoint.default = 'https://localhost:3001/v2/validate'; + await request(app) + .patch(`/config/updateRelay/${configId2}`) + .set('Authorization', `Bearer ${adminMasterAccountToken}`) + .send(bodyRelay).expect(200); + + // Request verification code for Relay 2 + response = await request(app) + .patch(`/domain/relay/verificationCode/${domainId}`) + .set('Authorization', `Bearer ${adminMasterAccountToken}`) + .send().expect(200); + + const code2 = response.body.code; + expect(code1).toBe(code2); + }); + }); \ No newline at end of file diff --git a/tests/relay.test.js b/tests/relay.test.js index a216deb..48797f3 100644 --- a/tests/relay.test.js +++ b/tests/relay.test.js @@ -467,7 +467,6 @@ describe('Testing Switcher Relay Verification', () => { // Config has a verified Relay const config = await Config.findById(configId).exec(); config.relay.verified.set(EnvType.DEFAULT, true); - config.relay.verification_code = '123'; await config.save(); expect(config.relay.verified.get(EnvType.DEFAULT)).toBe(true);