diff --git a/indexer/services/comlink/__tests__/helpers/helpers.ts b/indexer/services/comlink/__tests__/helpers/helpers.ts index f9abba3e88..d5fe46f5f6 100644 --- a/indexer/services/comlink/__tests__/helpers/helpers.ts +++ b/indexer/services/comlink/__tests__/helpers/helpers.ts @@ -26,6 +26,7 @@ export async function sendRequestToApp({ errorMsg, expressApp, expectedStatus = 200, + headers = {}, }: { type: RequestMethod, path: string, @@ -33,6 +34,7 @@ export async function sendRequestToApp({ errorMsg?: string, expressApp: e.Express, expectedStatus?: number, + headers?: Record, }) { let req: request.Test; @@ -53,6 +55,10 @@ export async function sendRequestToApp({ throw new Error(`Invalid type of request: ${type}`); } + if (Object.keys(headers).length) { + await req.set(headers); + } + const response: request.Response = await req.send(body); if (response.status !== expectedStatus) { console.log(response.body); // eslint-disable-line no-console diff --git a/indexer/services/comlink/__tests__/lib/compliance-and-geo-check.test.ts b/indexer/services/comlink/__tests__/lib/compliance-and-geo-check.test.ts new file mode 100644 index 0000000000..19e54bc7a8 --- /dev/null +++ b/indexer/services/comlink/__tests__/lib/compliance-and-geo-check.test.ts @@ -0,0 +1,229 @@ +import express from 'express'; + +import { BlockedCode, RequestMethod } from '../../src/types'; +import Server from '../../src/request-helpers/server'; +import { sendRequestToApp } from '../helpers/helpers'; +import { complianceAndGeoCheck } from '../../src/lib/compliance-and-geo-check'; +import { handleValidationErrors } from '../../src/request-helpers/error-handler'; +import { checkSchema } from 'express-validator'; +import { + ComplianceStatus, + ComplianceStatusTable, + dbHelpers, + testConstants, + testMocks, +} from '@dydxprotocol-indexer/postgres'; +import request from 'supertest'; +import { + INDEXER_COMPLIANCE_BLOCKED_PAYLOAD, + INDEXER_GEOBLOCKED_PAYLOAD, + isRestrictedCountryHeaders, +} from '@dydxprotocol-indexer/compliance'; +import config from '../../src/config'; + +jest.mock('@dydxprotocol-indexer/compliance'); + +// Create a router to test the middleware with +const router: express.Router = express.Router(); + +const restrictedHeaders = { + 'cf-ipcountry': 'US', +}; + +const nonRestrictedHeaders = { + 'cf-ipcountry': 'SA', +}; + +router.get( + '/check-compliance-query', + checkSchema({ + address: { + in: ['query'], + isString: true, + optional: true, + }, + }), + handleValidationErrors, + complianceAndGeoCheck, + (req: express.Request, res: express.Response) => { + res.sendStatus(200); + }, +); + +router.get( + '/check-compliance-param/:address', + checkSchema({ + address: { + in: ['params'], + isString: true, + }, + }), + handleValidationErrors, + complianceAndGeoCheck, + (req: express.Request, res: express.Response) => { + res.sendStatus(200); + }, +); + +export const complianceCheckApp = Server(router); + +describe('compliance-check', () => { + let isRestrictedCountrySpy: jest.SpyInstance; + + beforeAll(async () => { + config.INDEXER_LEVEL_GEOBLOCKING_ENABLED = true; + await dbHelpers.migrate(); + }); + + beforeEach(async () => { + isRestrictedCountrySpy = isRestrictedCountryHeaders as unknown as jest.Mock; + await testMocks.seedData(); + }); + + afterAll(async () => { + await dbHelpers.teardown(); + }); + + afterEach(async () => { + jest.restoreAllMocks(); + await dbHelpers.clearData(); + }); + + it.each([ + ['query', '/v4/check-compliance-query?address=random'], + ['param', '/v4/check-compliance-param/random'], + ])('does not return 403 if address in request is not in database (%s)', async ( + _name: string, + path: string, + ) => { + isRestrictedCountrySpy.mockReturnValueOnce(false); + await sendRequestToApp({ + type: RequestMethod.GET, + path, + expressApp: complianceCheckApp, + expectedStatus: 200, + }); + }); + + it.each([ + ['query', '/v4/check-compliance-query?address=random'], + ['param', '/v4/check-compliance-param/random'], + ])('does not return 403 if address in request is not in database (%s) and non-restricted country', async ( + _name: string, + path: string, + ) => { + isRestrictedCountrySpy.mockReturnValueOnce(false); + await sendRequestToApp({ + type: RequestMethod.GET, + path, + expressApp: complianceCheckApp, + expectedStatus: 200, + headers: nonRestrictedHeaders, + }); + }); + + it.each([ + ['query', `/v4/check-compliance-query?address=${testConstants.defaultAddress}`], + ['param', `/v4/check-compliance-param/${testConstants.defaultAddress}`], + ])('does not return 403 if address in request is not blocked (%s)', async ( + _name: string, + path: string, + ) => { + isRestrictedCountrySpy.mockReturnValueOnce(false); + await ComplianceStatusTable.create(testConstants.compliantStatusData); + await sendRequestToApp({ + type: RequestMethod.GET, + path, + expressApp: complianceCheckApp, + expectedStatus: 200, + }); + }); + + it.each([ + ['query', `/v4/check-compliance-query?address=${testConstants.defaultAddress}`], + ['param', `/v4/check-compliance-param/${testConstants.defaultAddress}`], + ])('does not return 403 if address in request is in CLOSE_ONLY (%s)', async ( + _name: string, + path: string, + ) => { + isRestrictedCountrySpy.mockReturnValueOnce(false); + await ComplianceStatusTable.create({ + ...testConstants.compliantStatusData, + status: ComplianceStatus.CLOSE_ONLY, + }); + await sendRequestToApp({ + type: RequestMethod.GET, + path, + expressApp: complianceCheckApp, + expectedStatus: 200, + }); + }); + + it.each([ + ['query', `/v4/check-compliance-query?address=${testConstants.defaultAddress}`], + ['param', `/v4/check-compliance-param/${testConstants.defaultAddress}`], + ])('does not return 403 if address in request is in CLOSE_ONLY and from restricted country (%s)', async ( + _name: string, + path: string, + ) => { + isRestrictedCountrySpy.mockReturnValueOnce(true); + await ComplianceStatusTable.create({ + ...testConstants.compliantStatusData, + status: ComplianceStatus.CLOSE_ONLY, + }); + await sendRequestToApp({ + type: RequestMethod.GET, + path, + expressApp: complianceCheckApp, + expectedStatus: 200, + }); + }); + + it.each([ + ['query', `/v4/check-compliance-query?address=${testConstants.defaultAddress}`], + ['param', `/v4/check-compliance-param/${testConstants.defaultAddress}`], + ])('does return 403 if request is from restricted country (%s)', async ( + _name: string, + path: string, + ) => { + isRestrictedCountrySpy.mockReturnValueOnce(true); + const response: request.Response = await sendRequestToApp({ + type: RequestMethod.GET, + path, + expressApp: complianceCheckApp, + expectedStatus: 403, + headers: restrictedHeaders, + }); + + expect(response.body).toEqual(expect.objectContaining({ + errors: expect.arrayContaining([{ + msg: INDEXER_GEOBLOCKED_PAYLOAD, + code: BlockedCode.GEOBLOCKED, + }]), + })); + }); + + it.each([ + ['query', `/v4/check-compliance-query?address=${testConstants.blockedAddress}`], + ['param', `/v4/check-compliance-param/${testConstants.blockedAddress}`], + ])('does return 403 if address in request is blocked (%s)', async ( + _name: string, + path: string, + ) => { + isRestrictedCountrySpy.mockReturnValueOnce(false); + await ComplianceStatusTable.create(testConstants.noncompliantStatusData); + const response: request.Response = await sendRequestToApp({ + type: RequestMethod.GET, + path, + expressApp: complianceCheckApp, + expectedStatus: 403, + }); + + expect(response.body).toEqual(expect.objectContaining({ + errors: expect.arrayContaining([{ + msg: INDEXER_COMPLIANCE_BLOCKED_PAYLOAD, + code: BlockedCode.COMPLIANCE_BLOCKED, + }]), + })); + }); +}); diff --git a/indexer/services/comlink/__tests__/lib/compliance-check.test.ts b/indexer/services/comlink/__tests__/lib/compliance-check.test.ts deleted file mode 100644 index 8a2d200872..0000000000 --- a/indexer/services/comlink/__tests__/lib/compliance-check.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import express from 'express'; - -import { BlockedCode, RequestMethod } from '../../src/types'; -import Server from '../../src/request-helpers/server'; -import { sendRequestToApp } from '../helpers/helpers'; -import { complianceCheck } from '../../src/lib/compliance-check'; -import { handleValidationErrors } from '../../src/request-helpers/error-handler'; -import { checkSchema } from 'express-validator'; -import { - ComplianceTable, dbHelpers, testConstants, testMocks, -} from '@dydxprotocol-indexer/postgres'; -import { blockedComplianceData, nonBlockedComplianceData } from '@dydxprotocol-indexer/postgres/build/__tests__/helpers/constants'; -import request from 'supertest'; -import { INDEXER_COMPLIANCE_BLOCKED_PAYLOAD } from '@dydxprotocol-indexer/compliance'; - -// Create a router to test the middleware with -const router: express.Router = express.Router(); - -router.get( - '/check-compliance-query', - checkSchema({ - address: { - in: ['query'], - isString: true, - optional: true, - }, - }), - handleValidationErrors, - complianceCheck, - (req: express.Request, res: express.Response) => { - res.sendStatus(200); - }, -); - -router.get( - '/check-compliance-param/:address', - checkSchema({ - address: { - in: ['params'], - isString: true, - }, - }), - handleValidationErrors, - complianceCheck, - (req: express.Request, res: express.Response) => { - res.sendStatus(200); - }, -); - -export const complianceCheckApp = Server(router); - -describe('compliance-check', () => { - beforeAll(async () => { - await dbHelpers.migrate(); - }); - - beforeEach(async () => { - await testMocks.seedData(); - }); - - afterAll(async () => { - await dbHelpers.teardown(); - }); - - afterEach(async () => { - await dbHelpers.clearData(); - }); - - it('does not return 403 if no address in request', async () => { - await sendRequestToApp({ - type: RequestMethod.GET, - path: '/v4/check-compliance-query', - expressApp: complianceCheckApp, - expectedStatus: 200, - }); - }); - - it.each([ - ['query', '/v4/check-compliance-query?address=random'], - ['param', '/v4/check-compliance-param/random'], - ])('does not return 403 if address in request is not in database (%s)', async ( - _name: string, - path: string, - ) => { - await sendRequestToApp({ - type: RequestMethod.GET, - path, - expressApp: complianceCheckApp, - expectedStatus: 200, - }); - }); - - it.each([ - ['query', `/v4/check-compliance-query?address=${testConstants.defaultAddress}`], - ['param', `/v4/check-compliance-param/${testConstants.defaultAddress}`], - ])('does not return 403 if address in request is not blocked (%s)', async ( - _name: string, - path: string, - ) => { - await ComplianceTable.create(nonBlockedComplianceData); - await sendRequestToApp({ - type: RequestMethod.GET, - path, - expressApp: complianceCheckApp, - expectedStatus: 200, - }); - }); - - it.each([ - ['query', `/v4/check-compliance-query?address=${testConstants.blockedAddress}`], - ['param', `/v4/check-compliance-param/${testConstants.blockedAddress}`], - ])('does return 403 if address in request is blocked (%s)', async ( - _name: string, - path: string, - ) => { - await ComplianceTable.create(blockedComplianceData); - const response: request.Response = await sendRequestToApp({ - type: RequestMethod.GET, - path, - expressApp: complianceCheckApp, - expectedStatus: 403, - }); - - expect(response.body).toEqual(expect.objectContaining({ - errors: expect.arrayContaining([{ - msg: INDEXER_COMPLIANCE_BLOCKED_PAYLOAD, - code: BlockedCode.COMPLIANCE_BLOCKED, - }]), - })); - }); -}); diff --git a/indexer/services/comlink/__tests__/lib/restrict-countries.test.ts b/indexer/services/comlink/__tests__/lib/restrict-countries.test.ts deleted file mode 100644 index ab6957154a..0000000000 --- a/indexer/services/comlink/__tests__/lib/restrict-countries.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { INDEXER_GEOBLOCKED_PAYLOAD, isRestrictedCountryHeaders } from '@dydxprotocol-indexer/compliance'; -import { testConstants } from '@dydxprotocol-indexer/postgres'; -import config from '../../src/config'; -import { rejectRestrictedCountries } from '../../src/lib/restrict-countries'; -import { BlockedCode } from '../../src/types'; -import * as utils from '../../src/lib/utils'; -import { matchedData } from 'express-validator'; - -jest.mock('@dydxprotocol-indexer/compliance'); -jest.mock('express-validator'); - -const restrictedHeaders = { - 'cf-ipcountry': 'US', -}; - -const nonRestrictedHeaders = { - 'cf-ipcountry': 'SA', -}; - -const internalIp: string = '3.125.3.24'; - -describe('rejectRestrictedCountries', () => { - let isRestrictedCountrySpy: jest.SpyInstance; - let matchedDataSpy: jest.SpyInstance; - let req: any; - let res: any; - let next: any; - - const defaultEnabled: boolean = config.INDEXER_LEVEL_GEOBLOCKING_ENABLED; - - beforeAll(() => { - config.INDEXER_LEVEL_GEOBLOCKING_ENABLED = true; - }); - - afterAll(() => { - config.INDEXER_LEVEL_GEOBLOCKING_ENABLED = defaultEnabled; - }); - - beforeEach(() => { - isRestrictedCountrySpy = isRestrictedCountryHeaders as unknown as jest.Mock; - matchedDataSpy = matchedData as unknown as jest.Mock; - req = { - get: jest.fn().mockReturnThis(), - }; - res = { - status: jest.fn().mockReturnThis(), - json: jest.fn().mockReturnThis(), - set: jest.fn().mockReturnThis(), - }; - next = jest.fn(); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('does not reject requests from non-restricted countries', async () => { - // non-restricted country in header - req.headers = nonRestrictedHeaders; - isRestrictedCountrySpy.mockReturnValueOnce(false); - matchedDataSpy.mockReturnValue({ address: testConstants.defaultAddress }); - - await rejectRestrictedCountries(req, res, next); - expect(res.status).not.toHaveBeenCalled(); - expect(next).toHaveBeenCalled(); - }); - - it('rejects request from restricted countries with a 403', async () => { - // restricted ipcountry - req.headers = restrictedHeaders; - isRestrictedCountrySpy.mockReturnValueOnce(true); - matchedDataSpy.mockReturnValue({ address: testConstants.defaultAddress }); - - await rejectRestrictedCountries(req, res, next); - expect(res.status).toHaveBeenCalledWith(403); - expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ - errors: expect.arrayContaining([ - { - msg: INDEXER_GEOBLOCKED_PAYLOAD, - code: BlockedCode.GEOBLOCKED, - }, - ]), - })); - expect(next).not.toHaveBeenCalled(); - }); - - it('does not check headers for internal indexer ip address', async () => { - // restricted ipcountry - req.headers = restrictedHeaders; - isRestrictedCountrySpy.mockReturnValueOnce(true); - matchedDataSpy.mockReturnValue({ address: testConstants.defaultAddress }); - jest.spyOn(utils, 'getIpAddr').mockReturnValue(internalIp); - jest.spyOn(utils, 'isIndexerIp').mockImplementation((ip: string): boolean => ip === internalIp); - - await rejectRestrictedCountries(req, res, next); - expect(res.status).not.toHaveBeenCalled(); - expect(next).toHaveBeenCalled(); - }); -}); diff --git a/indexer/services/comlink/package.json b/indexer/services/comlink/package.json index 82352d664a..b6d2327691 100644 --- a/indexer/services/comlink/package.json +++ b/indexer/services/comlink/package.json @@ -7,6 +7,7 @@ "start": "node --heapsnapshot-signal=SIGUSR2 -r dd-trace/init -r dotenv-flow/config build/src/index.js", "dev": "node -r dotenv-flow/config build/src/index.js", "build": "rm -rf build/ && tsc && pnpm run swagger && pnpm run gen-markdown", + "build:dev": "rm -rf build/ && tsc", "build:prod": "pnpm run build", "build:watch": "pnpm run build -- --watch", "coverage": "pnpm test -- --coverage", diff --git a/indexer/services/comlink/src/controllers/api/v4/addresses-controller.ts b/indexer/services/comlink/src/controllers/api/v4/addresses-controller.ts index 8d7e833921..c2f4964fc0 100644 --- a/indexer/services/comlink/src/controllers/api/v4/addresses-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/addresses-controller.ts @@ -37,7 +37,7 @@ import { import { getReqRateLimiter } from '../../../caches/rate-limiters'; import config from '../../../config'; -import { complianceCheck } from '../../../lib/compliance-check'; +import { complianceAndGeoCheck } from '../../../lib/compliance-and-geo-check'; import { NotFoundError } from '../../../lib/errors'; import { adjustUSDCAssetPosition, @@ -51,7 +51,6 @@ import { initializePerpetualPositionsWithFunding, } from '../../../lib/helpers'; import { rateLimiterMiddleware } from '../../../lib/rate-limit'; -import { rejectRestrictedCountries } from '../../../lib/restrict-countries'; import { CheckAddressSchema, CheckSubaccountSchema } from '../../../lib/validation/schemas'; import { handleValidationErrors } from '../../../request-helpers/error-handler'; import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats'; @@ -246,11 +245,10 @@ class AddressesController extends Controller { router.get( '/:address', - rejectRestrictedCountries, rateLimiterMiddleware(getReqRateLimiter), ...CheckAddressSchema, handleValidationErrors, - complianceCheck, + complianceAndGeoCheck, ExportResponseCodeStats({ controllerName }), async (req: express.Request, res: express.Response) => { const { @@ -280,11 +278,10 @@ router.get( router.get( '/:address/subaccountNumber/:subaccountNumber', - rejectRestrictedCountries, rateLimiterMiddleware(getReqRateLimiter), ...CheckSubaccountSchema, handleValidationErrors, - complianceCheck, + complianceAndGeoCheck, ExportResponseCodeStats({ controllerName }), async (req: express.Request, res: express.Response) => { const start: number = Date.now(); diff --git a/indexer/services/comlink/src/controllers/api/v4/asset-positions-controller.ts b/indexer/services/comlink/src/controllers/api/v4/asset-positions-controller.ts index ad28f84796..3aca9f0796 100644 --- a/indexer/services/comlink/src/controllers/api/v4/asset-positions-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/asset-positions-controller.ts @@ -25,7 +25,7 @@ import { import { getReqRateLimiter } from '../../../caches/rate-limiters'; import config from '../../../config'; -import { complianceCheck } from '../../../lib/compliance-check'; +import { complianceAndGeoCheck } from '../../../lib/compliance-and-geo-check'; import { adjustUSDCAssetPosition, filterAssetPositions, @@ -34,7 +34,6 @@ import { handleControllerError, } from '../../../lib/helpers'; import { rateLimiterMiddleware } from '../../../lib/rate-limit'; -import { rejectRestrictedCountries } from '../../../lib/restrict-countries'; import { CheckSubaccountSchema, } from '../../../lib/validation/schemas'; @@ -148,11 +147,10 @@ class AddressesController extends Controller { router.get( '/', - rejectRestrictedCountries, rateLimiterMiddleware(getReqRateLimiter), ...CheckSubaccountSchema, handleValidationErrors, - complianceCheck, + complianceAndGeoCheck, ExportResponseCodeStats({ controllerName }), async (req: express.Request, res: express.Response) => { const start: number = Date.now(); diff --git a/indexer/services/comlink/src/controllers/api/v4/fills-controller.ts b/indexer/services/comlink/src/controllers/api/v4/fills-controller.ts index 0a241c8e78..052e3c349d 100644 --- a/indexer/services/comlink/src/controllers/api/v4/fills-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/fills-controller.ts @@ -21,13 +21,12 @@ import { import { getReqRateLimiter } from '../../../caches/rate-limiters'; import config from '../../../config'; -import { complianceCheck } from '../../../lib/compliance-check'; +import { complianceAndGeoCheck } from '../../../lib/compliance-and-geo-check'; import { NotFoundError } from '../../../lib/errors'; import { getClobPairId, handleControllerError, isDefined, } from '../../../lib/helpers'; import { rateLimiterMiddleware } from '../../../lib/rate-limit'; -import { rejectRestrictedCountries } from '../../../lib/restrict-countries'; import { CheckLimitAndCreatedBeforeOrAtSchema, CheckSubaccountSchema } from '../../../lib/validation/schemas'; import { handleValidationErrors } from '../../../request-helpers/error-handler'; import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats'; @@ -99,7 +98,6 @@ class FillsController extends Controller { router.get( '/', - rejectRestrictedCountries, rateLimiterMiddleware(getReqRateLimiter), ...CheckSubaccountSchema, ...CheckLimitAndCreatedBeforeOrAtSchema, @@ -127,7 +125,7 @@ router.get( }, }), handleValidationErrors, - complianceCheck, + complianceAndGeoCheck, ExportResponseCodeStats({ controllerName }), async (req: express.Request, res: express.Response) => { const start: number = Date.now(); diff --git a/indexer/services/comlink/src/controllers/api/v4/historical-block-trading-rewards-controller.ts b/indexer/services/comlink/src/controllers/api/v4/historical-block-trading-rewards-controller.ts index 854d73e168..2f09cb547b 100644 --- a/indexer/services/comlink/src/controllers/api/v4/historical-block-trading-rewards-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/historical-block-trading-rewards-controller.ts @@ -17,7 +17,6 @@ import { getReqRateLimiter } from '../../../caches/rate-limiters'; import config from '../../../config'; import { handleControllerError } from '../../../lib/helpers'; import { rateLimiterMiddleware } from '../../../lib/rate-limit'; -import { rejectRestrictedCountries } from '../../../lib/restrict-countries'; import { CheckHistoricalBlockTradingRewardsSchema } from '../../../lib/validation/schemas'; import { handleValidationErrors } from '../../../request-helpers/error-handler'; import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats'; @@ -55,7 +54,6 @@ class HistoricalBlockTradingRewardsController extends Controller { router.get( '/:address', - rejectRestrictedCountries, rateLimiterMiddleware(getReqRateLimiter), ...CheckHistoricalBlockTradingRewardsSchema, handleValidationErrors, diff --git a/indexer/services/comlink/src/controllers/api/v4/historical-pnl-controller.ts b/indexer/services/comlink/src/controllers/api/v4/historical-pnl-controller.ts index e2045389e1..3241557a95 100644 --- a/indexer/services/comlink/src/controllers/api/v4/historical-pnl-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/historical-pnl-controller.ts @@ -17,11 +17,10 @@ import { import { getReqRateLimiter } from '../../../caches/rate-limiters'; import config from '../../../config'; -import { complianceCheck } from '../../../lib/compliance-check'; +import { complianceAndGeoCheck } from '../../../lib/compliance-and-geo-check'; import { NotFoundError } from '../../../lib/errors'; import { handleControllerError } from '../../../lib/helpers'; import { rateLimiterMiddleware } from '../../../lib/rate-limit'; -import { rejectRestrictedCountries } from '../../../lib/restrict-countries'; import { CheckLimitAndCreatedBeforeOrAtAndOnOrAfterSchema, CheckSubaccountSchema, @@ -91,12 +90,11 @@ class HistoricalPnlController extends Controller { router.get( '/', - rejectRestrictedCountries, rateLimiterMiddleware(getReqRateLimiter), ...CheckSubaccountSchema, ...CheckLimitAndCreatedBeforeOrAtAndOnOrAfterSchema, handleValidationErrors, - complianceCheck, + complianceAndGeoCheck, ExportResponseCodeStats({ controllerName }), async (req: express.Request, res: express.Response) => { const start: number = Date.now(); diff --git a/indexer/services/comlink/src/controllers/api/v4/historical-trading-reward-aggregations-controller.ts b/indexer/services/comlink/src/controllers/api/v4/historical-trading-reward-aggregations-controller.ts index 4bf1237406..56f85973d4 100644 --- a/indexer/services/comlink/src/controllers/api/v4/historical-trading-reward-aggregations-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/historical-trading-reward-aggregations-controller.ts @@ -18,7 +18,6 @@ import { getReqRateLimiter } from '../../../caches/rate-limiters'; import config from '../../../config'; import { handleControllerError } from '../../../lib/helpers'; import { rateLimiterMiddleware } from '../../../lib/rate-limit'; -import { rejectRestrictedCountries } from '../../../lib/restrict-countries'; import { CheckHistoricalBlockTradingRewardsSchema } from '../../../lib/validation/schemas'; import { handleValidationErrors } from '../../../request-helpers/error-handler'; import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats'; @@ -57,7 +56,6 @@ class HistoricalTradingRewardAggregationsController extends Controller { router.get( '/:address', - rejectRestrictedCountries, rateLimiterMiddleware(getReqRateLimiter), ...CheckHistoricalBlockTradingRewardsSchema, ...checkSchema({ diff --git a/indexer/services/comlink/src/controllers/api/v4/orders-controller.ts b/indexer/services/comlink/src/controllers/api/v4/orders-controller.ts index 19466174cb..606f67fb7f 100644 --- a/indexer/services/comlink/src/controllers/api/v4/orders-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/orders-controller.ts @@ -28,13 +28,12 @@ import { import { getReqRateLimiter } from '../../../caches/rate-limiters'; import config from '../../../config'; import { redisClient } from '../../../helpers/redis/redis-controller'; -import { complianceCheck } from '../../../lib/compliance-check'; +import { complianceAndGeoCheck } from '../../../lib/compliance-and-geo-check'; import { NotFoundError } from '../../../lib/errors'; import { handleControllerError, } from '../../../lib/helpers'; import { rateLimiterMiddleware } from '../../../lib/rate-limit'; -import { rejectRestrictedCountries } from '../../../lib/restrict-countries'; import { CheckLimitSchema, CheckSubaccountSchema, @@ -167,7 +166,6 @@ class OrdersController extends Controller { router.get( '/', - rejectRestrictedCountries, rateLimiterMiddleware(getReqRateLimiter), ...CheckSubaccountSchema, ...CheckLimitSchema, @@ -224,7 +222,7 @@ router.get( query('goodTilBlock').if(query('goodTilBlockTime').exists()).isEmpty() .withMessage('Cannot provide both goodTilBlock and goodTilBlockTime'), handleValidationErrors, - complianceCheck, + complianceAndGeoCheck, ExportResponseCodeStats({ controllerName }), async (req: express.Request, res: express.Response) => { const start: number = Date.now(); @@ -276,7 +274,6 @@ router.get( router.get( '/:orderId', - rejectRestrictedCountries, ...checkSchema({ orderId: { in: ['params'], diff --git a/indexer/services/comlink/src/controllers/api/v4/perpetual-positions-controller.ts b/indexer/services/comlink/src/controllers/api/v4/perpetual-positions-controller.ts index eadba62063..ca7f66c9c4 100644 --- a/indexer/services/comlink/src/controllers/api/v4/perpetual-positions-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/perpetual-positions-controller.ts @@ -29,7 +29,7 @@ import { import { getReqRateLimiter } from '../../../caches/rate-limiters'; import config from '../../../config'; -import { complianceCheck } from '../../../lib/compliance-check'; +import { complianceAndGeoCheck } from '../../../lib/compliance-and-geo-check'; import { NotFoundError } from '../../../lib/errors'; import { getFundingIndexMaps, @@ -38,7 +38,6 @@ import { initializePerpetualPositionsWithFunding, } from '../../../lib/helpers'; import { rateLimiterMiddleware } from '../../../lib/rate-limit'; -import { rejectRestrictedCountries } from '../../../lib/restrict-countries'; import { CheckLimitAndCreatedBeforeOrAtSchema, CheckSubaccountSchema, @@ -150,7 +149,6 @@ class PerpetualPositionsController extends Controller { router.get( '/', - rejectRestrictedCountries, rateLimiterMiddleware(getReqRateLimiter), ...CheckSubaccountSchema, ...CheckLimitAndCreatedBeforeOrAtSchema, @@ -168,7 +166,7 @@ router.get( }, }), handleValidationErrors, - complianceCheck, + complianceAndGeoCheck, ExportResponseCodeStats({ controllerName }), async (req: express.Request, res: express.Response) => { const start: number = Date.now(); diff --git a/indexer/services/comlink/src/controllers/api/v4/transfers-controller.ts b/indexer/services/comlink/src/controllers/api/v4/transfers-controller.ts index 860d9019e8..11dea9994e 100644 --- a/indexer/services/comlink/src/controllers/api/v4/transfers-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/transfers-controller.ts @@ -23,11 +23,10 @@ import { import { getReqRateLimiter } from '../../../caches/rate-limiters'; import config from '../../../config'; -import { complianceCheck } from '../../../lib/compliance-check'; +import { complianceAndGeoCheck } from '../../../lib/compliance-and-geo-check'; import { NotFoundError } from '../../../lib/errors'; import { handleControllerError } from '../../../lib/helpers'; import { rateLimiterMiddleware } from '../../../lib/rate-limit'; -import { rejectRestrictedCountries } from '../../../lib/restrict-countries'; import { CheckLimitAndCreatedBeforeOrAtSchema, CheckSubaccountSchema } from '../../../lib/validation/schemas'; import { handleValidationErrors } from '../../../request-helpers/error-handler'; import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats'; @@ -130,12 +129,11 @@ class TransfersController extends Controller { router.get( '/', - rejectRestrictedCountries, rateLimiterMiddleware(getReqRateLimiter), ...CheckSubaccountSchema, ...CheckLimitAndCreatedBeforeOrAtSchema, handleValidationErrors, - complianceCheck, + complianceAndGeoCheck, ExportResponseCodeStats({ controllerName }), async (req: express.Request, res: express.Response) => { const start: number = Date.now(); diff --git a/indexer/services/comlink/src/lib/restrict-countries.ts b/indexer/services/comlink/src/lib/compliance-and-geo-check.ts similarity index 70% rename from indexer/services/comlink/src/lib/restrict-countries.ts rename to indexer/services/comlink/src/lib/compliance-and-geo-check.ts index dbb4be2ccc..3fee2e5135 100644 --- a/indexer/services/comlink/src/lib/restrict-countries.ts +++ b/indexer/services/comlink/src/lib/compliance-and-geo-check.ts @@ -17,9 +17,17 @@ import { create4xxResponse } from './helpers'; import { getIpAddr, isIndexerIp } from './utils'; /** - * Return an error code for users that access the API from a restricted country + * Checks if the address in the request is blocked or not. + * + * IF the address is in the compliance_status table and has the status CLOSE_ONLY, + * return data for the endpoint ELSE + * IF the address has compliance_status of BLOCKED block access to the endpoint (return 403) ELSE + * IF the origin country is restricted geography, block access to the endpoint (return 403) ELSE + * return data for the endpoint + * NOTE: This middleware must be used after `checkSchema` to ensure `matchData` can get the + * address parameter from the request. */ -export async function rejectRestrictedCountries( +export async function complianceAndGeoCheck( req: express.Request, res: express.Response, next: express.NextFunction, @@ -31,12 +39,7 @@ export async function rejectRestrictedCountries( return next(); } - const { - address, - }: { - address: string, - } = matchedData(req) as AddressRequest; - console.log('address', address); + const { address }: AddressRequest = matchedData(req) as AddressRequest; const updatedStatus: ComplianceStatusFromDatabase[] = await ComplianceStatusTable.findAll( { address: [address] }, [], diff --git a/indexer/services/comlink/src/lib/compliance-check.ts b/indexer/services/comlink/src/lib/compliance-check.ts deleted file mode 100644 index 0aa90fa981..0000000000 --- a/indexer/services/comlink/src/lib/compliance-check.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { INDEXER_COMPLIANCE_BLOCKED_PAYLOAD } from '@dydxprotocol-indexer/compliance'; -import { ComplianceDataFromDatabase, ComplianceTable } from '@dydxprotocol-indexer/postgres'; -import express from 'express'; -import { matchedData } from 'express-validator'; - -import { AddressRequest, BlockedCode } from '../types'; -import { create4xxResponse } from './helpers'; - -/** - * Checks if the address in the request is blocked or not. - * Returns 403 if the address is blocked, otherwise if there is no address in the request, or the - * address does not exist in the database or is not blocked, continue onto the next middleware. - * NOTE: This middleware must be used after `checkSchema` to ensure `matchData` can get the - * address parameter from the request. - */ -export async function complianceCheck( - req: express.Request, - res: express.Response, - next: express.NextFunction, -) { - // Check for the address parameter in either query params or path params - const { address }: AddressRequest = matchedData(req) as AddressRequest; - if (address === undefined) { - return next(); - } - - // Search for any compliance data indicating the address is blocked - const complianceData: ComplianceDataFromDatabase[] = await ComplianceTable.findAll( - { address: [address], blocked: true }, - [], - ); - if (complianceData.length > 0) { - return create4xxResponse( - res, - INDEXER_COMPLIANCE_BLOCKED_PAYLOAD, - 403, - { code: BlockedCode.COMPLIANCE_BLOCKED }, - ); - } - - return next(); -}