Skip to content

Commit

Permalink
Cherry-pick read only changes into v3.x release (#1143)
Browse files Browse the repository at this point in the history
* remove geoblocking from Indexer endpoints (#1029)

* [OTE-130] implement new geo-blocking strategy for socks (#1049)
  • Loading branch information
dydxwill authored Mar 5, 2024
1 parent 5a98a9d commit e21eb83
Show file tree
Hide file tree
Showing 15 changed files with 29 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,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 { CheckLimitSchema, CheckTickerParamSchema } from '../../../lib/validation/schemas';
import { handleValidationErrors } from '../../../request-helpers/error-handler';
import { candleToResponseObject } from '../../../request-helpers/request-transformer';
Expand Down Expand Up @@ -50,7 +49,6 @@ class CandleController extends Controller {

router.get(
'/perpetualMarkets/:ticker',
rejectRestrictedCountries,
rateLimiterMiddleware(getReqRateLimiter),
...CheckLimitSchema,
...CheckTickerParamSchema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import config from '../../../config';
import { complianceProvider } from '../../../helpers/compliance/compliance-clients';
import { create4xxResponse, handleControllerError } from '../../../lib/helpers';
import { rateLimiterMiddleware } from '../../../lib/rate-limit';
import { rejectRestrictedCountries } from '../../../lib/restrict-countries';
import { getIpAddr } from '../../../lib/utils';
import { handleValidationErrors } from '../../../request-helpers/error-handler';
import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats';
Expand Down Expand Up @@ -101,7 +100,6 @@ class ComplianceController extends Controller {

router.get(
'/',
rejectRestrictedCountries,
rateLimiterMiddleware(getReqRateLimiter),
...checkSchema({
address: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import config from '../../../config';
import { NotFoundError } from '../../../lib/errors';
import { handleControllerError } from '../../../lib/helpers';
import { rateLimiterMiddleware } from '../../../lib/rate-limit';
import { rejectRestrictedCountries } from '../../../lib/restrict-countries';
import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats';
import { HeightResponse } from '../../../types';

Expand All @@ -34,7 +33,6 @@ class HeightController extends Controller {

router.get(
'/',
rejectRestrictedCountries,
rateLimiterMiddleware(getReqRateLimiter),
ExportResponseCodeStats({ controllerName }),
async (req: express.Request, res: express.Response) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import config from '../../../config';
import { NotFoundError } from '../../../lib/errors';
import { handleControllerError } from '../../../lib/helpers';
import { rateLimiterMiddleware } from '../../../lib/rate-limit';
import { rejectRestrictedCountries } from '../../../lib/restrict-countries';
import { CheckEffectiveBeforeOrAtSchema, CheckLimitSchema, CheckTickerParamSchema } from '../../../lib/validation/schemas';
import { handleValidationErrors } from '../../../request-helpers/error-handler';
import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats';
Expand Down Expand Up @@ -80,7 +79,6 @@ class HistoricalFundingController extends Controller {

router.get(
'/:ticker',
rejectRestrictedCountries,
rateLimiterMiddleware(getReqRateLimiter),
...CheckLimitSchema,
...CheckTickerParamSchema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { redisClient } from '../../../helpers/redis/redis-controller';
import { NotFoundError } from '../../../lib/errors';
import { handleControllerError } from '../../../lib/helpers';
import { rateLimiterMiddleware } from '../../../lib/rate-limit';
import { rejectRestrictedCountries } from '../../../lib/restrict-countries';
import { handleValidationErrors } from '../../../request-helpers/error-handler';
import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats';
import { OrderbookLevelsToResponseObject } from '../../../request-helpers/request-transformer';
Expand Down Expand Up @@ -53,7 +52,6 @@ class OrderbookController extends Controller {

router.get(
'/perpetualMarket/:ticker',
rejectRestrictedCountries,
rateLimiterMiddleware(getReqRateLimiter),
...checkSchema({
ticker: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
handleControllerError,
} from '../../../lib/helpers';
import { rateLimiterMiddleware } from '../../../lib/rate-limit';
import { rejectRestrictedCountries } from '../../../lib/restrict-countries';
import { CheckLimitSchema, CheckTickerOptionalQuerySchema } from '../../../lib/validation/schemas';
import { handleValidationErrors } from '../../../request-helpers/error-handler';
import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats';
Expand Down Expand Up @@ -113,7 +112,6 @@ class PerpetualMarketsController extends Controller {

router.get(
'/',
rejectRestrictedCountries,
rateLimiterMiddleware(getReqRateLimiter),
...CheckLimitSchema,
...CheckTickerOptionalQuerySchema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import config from '../../../config';
import { SPARKLINE_TIME_PERIOD_TO_LIMIT_MAP, SPARKLINE_TIME_PERIOD_TO_RESOLUTION_MAP } from '../../../lib/constants';
import { handleControllerError } from '../../../lib/helpers';
import { rateLimiterMiddleware } from '../../../lib/rate-limit';
import { rejectRestrictedCountries } from '../../../lib/restrict-countries';
import { handleValidationErrors } from '../../../request-helpers/error-handler';
import { candlesToSparklineResponseObject } from '../../../request-helpers/request-transformer';
import { SparklineResponseObject, SparklinesRequest, SparklineTimePeriod } from '../../../types';
Expand Down Expand Up @@ -59,7 +58,6 @@ class FillsController extends Controller {

router.get(
'/',
rejectRestrictedCountries,
rateLimiterMiddleware(getReqRateLimiter),
...checkSchema({
timePeriod: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Controller, Get, Route } from 'tsoa';

import { getReqRateLimiter } from '../../../caches/rate-limiters';
import { rateLimiterMiddleware } from '../../../lib/rate-limit';
import { rejectRestrictedCountries } from '../../../lib/restrict-countries';
import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats';
import { TimeResponse } from '../../../types';

Expand All @@ -26,7 +25,6 @@ class TimeController extends Controller {

router.get(
'/',
rejectRestrictedCountries,
rateLimiterMiddleware(getReqRateLimiter),
ExportResponseCodeStats({ controllerName }),
(_req: express.Request, res: express.Response) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
handleControllerError,
} from '../../../lib/helpers';
import { rateLimiterMiddleware } from '../../../lib/rate-limit';
import { rejectRestrictedCountries } from '../../../lib/restrict-countries';
import { CheckLimitAndCreatedBeforeOrAtSchema } from '../../../lib/validation/schemas';
import { handleValidationErrors } from '../../../request-helpers/error-handler';
import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats';
Expand Down Expand Up @@ -77,7 +76,6 @@ class TradesController extends Controller {

router.get(
'/perpetualMarket/:ticker',
rejectRestrictedCountries,
rateLimiterMiddleware(getReqRateLimiter),
...CheckLimitAndCreatedBeforeOrAtSchema,
...checkSchema({
Expand Down
50 changes: 9 additions & 41 deletions indexer/services/socks/__tests__/lib/subscriptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { btcTicker, invalidChannel, invalidTicker } from '../constants';
import { axiosRequest } from '../../src/lib/axios';
import { AxiosSafeServerError, makeAxiosSafeServerError } from '@dydxprotocol-indexer/base';
import { BlockedError } from '../../src/lib/errors';
import { isRestrictedCountry } from '@dydxprotocol-indexer/compliance';

jest.mock('ws');
jest.mock('../../src/helpers/wss');
Expand Down Expand Up @@ -58,8 +57,7 @@ describe('Subscriptions', () => {
[Channel.V4_TRADES]: ['/v4/trades/perpetualMarket/.+'],
};
const initialMessage: Object = { a: 'b' };
const restrictedCountry: string = 'US';
const nonRestrictedCountry: string = 'AR';
const country: string = 'AR';

beforeAll(async () => {
await dbHelpers.migrate();
Expand All @@ -83,9 +81,6 @@ describe('Subscriptions', () => {
axiosRequestMock = (axiosRequest as jest.Mock);
axiosRequestMock.mockClear();
axiosRequestMock.mockImplementation(() => (JSON.stringify(initialMessage)));
(isRestrictedCountry as jest.Mock).mockImplementation((country: string): boolean => {
return country === restrictedCountry;
});
});

describe('subscribe', () => {
Expand All @@ -106,7 +101,7 @@ describe('Subscriptions', () => {
initialMsgId,
id,
false,
nonRestrictedCountry,
country,
);

expect(sendMessageStringMock).toHaveBeenCalledTimes(1);
Expand All @@ -126,6 +121,9 @@ describe('Subscriptions', () => {
for (const urlPattern of urlPatterns) {
expect(axiosRequestMock).toHaveBeenCalledWith(expect.objectContaining({
url: expect.stringMatching(RegExp(urlPattern)),
headers: {
'cf-ipcountry': country,
},
}));
}
} else {
Expand All @@ -150,7 +148,6 @@ describe('Subscriptions', () => {
initialMsgId,
id,
false,
nonRestrictedCountry,
);

expect(sendMessageMock).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -179,7 +176,6 @@ describe('Subscriptions', () => {
initialMsgId,
defaultId,
false,
nonRestrictedCountry,
);
},
).rejects.toEqual(new Error(`Invalid channel: ${invalidChannel}`));
Expand All @@ -194,7 +190,6 @@ describe('Subscriptions', () => {
initialMsgId,
mockSubaccountId,
false,
nonRestrictedCountry,
);

expect(sendMessageMock).toHaveBeenCalledTimes(1);
Expand All @@ -217,7 +212,6 @@ describe('Subscriptions', () => {
initialMsgId,
mockSubaccountId,
false,
nonRestrictedCountry,
);

expect(sendMessageMock).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -253,32 +247,7 @@ describe('Subscriptions', () => {
initialMsgId,
mockSubaccountId,
false,
nonRestrictedCountry,
);

expect(sendMessageMock).toHaveBeenCalledTimes(1);
expect(sendMessageMock).toHaveBeenCalledWith(
mockWs,
connectionId,
expect.objectContaining({
connection_id: connectionId,
type: 'error',
message: expectedError.message,
}));
expect(subscriptions.subscriptions[Channel.V4_ACCOUNTS]).toBeUndefined();
expect(subscriptions.subscriptionLists[connectionId]).toBeUndefined();
});

it('sends blocked error if subscribing to subaccount from restricted country', async () => {
const expectedError: BlockedError = new BlockedError();
await subscriptions.subscribe(
mockWs,
Channel.V4_ACCOUNTS,
connectionId,
initialMsgId,
mockSubaccountId,
false,
restrictedCountry,
country,
);

expect(sendMessageMock).toHaveBeenCalledTimes(1);
Expand All @@ -305,7 +274,7 @@ describe('Subscriptions', () => {
initialMsgId,
mockSubaccountId,
false,
nonRestrictedCountry,
country,
);

expect(sendMessageStringMock).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -342,7 +311,7 @@ describe('Subscriptions', () => {
initialMsgId,
id,
false,
nonRestrictedCountry,
country,
);
subscriptions.unsubscribe(
connectionId,
Expand All @@ -362,7 +331,7 @@ describe('Subscriptions', () => {
initialMsgId,
mockSubaccountId,
false,
nonRestrictedCountry,
country,
);
subscriptions.unsubscribe(
connectionId,
Expand All @@ -386,7 +355,6 @@ describe('Subscriptions', () => {
initialMsgId,
validIds[channel],
false,
nonRestrictedCountry,
);
}));

Expand Down
47 changes: 1 addition & 46 deletions indexer/services/socks/__tests__/websocket/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ import {
} from '../../src/types';
import { InvalidMessageHandler } from '../../src/lib/invalid-message';
import { PingHandler } from '../../src/lib/ping';
import config from '../../src/config';
import { isRestrictedCountryHeaders, COUNTRY_HEADER_KEY } from '@dydxprotocol-indexer/compliance';
import { COUNTRY_HEADER_KEY } from '@dydxprotocol-indexer/compliance';

jest.mock('uuid');
jest.mock('../../src/helpers/wss');
jest.mock('../../src/lib/subscription');
jest.mock('../../src/lib/invalid-message');
jest.mock('../../src/lib/ping');
jest.mock('@dydxprotocol-indexer/compliance');

describe('Index', () => {
let index: Index;
Expand All @@ -32,12 +30,10 @@ describe('Index', () => {
let mockConnect: (ws: WebSocket, req: IncomingMessage) => void;
let wsOnSpy: jest.SpyInstance;
let wsPingSpy: jest.SpyInstance;
let wsTerminateSpy: jest.SpyInstance;
let invalidMsgHandlerSpy: jest.SpyInstance;
let pingHandlerSpy: jest.SpyInstance;

const connectionId: string = 'conId';
const defaultGeoblockingEnabled: boolean = config.INDEXER_LEVEL_GEOBLOCKING_ENABLED;
const countryCode: string = 'AR';

beforeAll(() => {
Expand All @@ -58,7 +54,6 @@ describe('Index', () => {
websocket = new WebSocket(null);
wsOnSpy = jest.spyOn(websocket, 'on');
wsPingSpy = jest.spyOn(websocket, 'ping').mockImplementation(jest.fn());
wsTerminateSpy = jest.spyOn(websocket, 'terminate').mockImplementation(jest.fn());
mockWss.onConnection = jest.fn().mockImplementation(
(cb: (ws: WebSocket, req: IncomingMessage) => void) => {
mockConnect = cb;
Expand Down Expand Up @@ -97,46 +92,6 @@ describe('Index', () => {
}),
);
});

describe('geoblocking', () => {
const isRestrictedCountrySpy: jest.Mock = isRestrictedCountryHeaders as unknown as jest.Mock;

beforeAll(() => {
config.INDEXER_LEVEL_GEOBLOCKING_ENABLED = true;
});

afterAll(() => {
config.INDEXER_LEVEL_GEOBLOCKING_ENABLED = defaultGeoblockingEnabled;
});

it('rejects connection if from restricted country', () => {
jest.spyOn(websocket, 'terminate').mockImplementation(jest.fn());
// restricted country headers
isRestrictedCountrySpy.mockReturnValue(true);

const message: IncomingMessage = new IncomingMessage(new Socket());
mockConnect(websocket, message);
expect(websocket.terminate).toHaveBeenCalled();
expect(Object.keys(index.connections)).toHaveLength(0);
expect(wsOnSpy).not.toHaveBeenCalled();
expect(wsTerminateSpy).toHaveBeenCalled();
expect(sendMessage).not.toHaveBeenCalled();
});

it('does not reject connection if from restricted country', () => {
(v4 as unknown as jest.Mock).mockReturnValueOnce(connectionId);
// non-restricted country headers
isRestrictedCountrySpy.mockReturnValue(false);

const message: IncomingMessage = new IncomingMessage(new Socket());
mockConnect(websocket, message);

// Test that the connection is tracked.
expect(index.connections[connectionId]).not.toBeUndefined();
expect(index.connections[connectionId].ws).toEqual(websocket);
expect(index.connections[connectionId].messageId).toEqual(0);
});
});
});

describe('handlers', () => {
Expand Down
8 changes: 8 additions & 0 deletions indexer/services/socks/src/helpers/header-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { CountryHeaders } from '@dydxprotocol-indexer/compliance';

import { IncomingMessage } from '../types';

export function getCountry(req: IncomingMessage): string | undefined {
const countryHeaders: CountryHeaders = req.headers as CountryHeaders;
return countryHeaders['cf-ipcountry'];
}
Loading

0 comments on commit e21eb83

Please sign in to comment.