Skip to content

Commit

Permalink
PROTO-1646: fix app verify in relay (#7595)
Browse files Browse the repository at this point in the history
  • Loading branch information
alecsavvy authored Feb 14, 2024
1 parent c9c9244 commit 7af96f0
Show file tree
Hide file tree
Showing 12 changed files with 612 additions and 229 deletions.
125 changes: 125 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"build": "tsc",
"build:watch": "tsc --watch",
"clean": "rm -rf dist",
"dev": "nodemon ./dist/index.js",
"dev": "concurrently \"npm:build:watch\" \"nodemon ./dist/index.js\"",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"lint": "tsc --noEmit && eslint \"src/**/*.ts*\"",
"sandbox": "npx ts-node ./src/scripts/sandbox.ts",
Expand Down Expand Up @@ -48,6 +48,7 @@
"@types/node": "^15.12.2",
"@types/supertest": "^2.0.11",
"@types/uuid": "^9.0.2",
"concurrently": "^8.2.2",
"esbuild": "^0.14.38",
"esbuild-register": "^3.3.2",
"eslint": "^7.32.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,6 @@ export class RelayRateLimiter {
throw blockedRes;
}
const pointsToConsume = rateLimiter.points / amountOfAllowedRequests;
logger.info({
constructedKey,
pointsToConsume,
totalPoints: rateLimiter.points,
amountOfAllowedRequests,
title: "rate-limit-math",
});
return rateLimiter.consume(constructedKey, pointsToConsume);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { logger } from '../logger'
import { NextFunction, Request, Response } from 'express'
import { config } from '..'
import { antiAbuseError, internalError } from '../error'
import { decodeAbi } from '../abi'
import { readAAOState, storeAAOState } from '../redis'
import { StatusCodes } from 'http-status-codes'

Expand All @@ -28,23 +27,14 @@ export const antiAbuseMiddleware = async (
next: NextFunction
) => {
const aaoConfig = config.aao
const { ip, recoveredSigner: user } = response.locals.ctx
const decodedAbi = decodeAbi(
response.locals.ctx.validatedRelayRequest.encodedABI
)
const isUserCreate =
decodedAbi.action === 'Create' && decodedAbi.entityType === 'User'
const isUserDeactivate =
user.is_deactivated === false &&
decodedAbi.action === 'Update' &&
decodedAbi.entityType === 'User' &&
JSON.parse(decodedAbi.metadata).data.is_deactivated === true
// User creations must be allowed as AAO won't have the user yet,
// and deactivations must be allowed even for abuse.
if (isUserCreate || isUserDeactivate) {
const { ip, recoveredSigner, signerIsApp, createOrDeactivate} = response.locals.ctx

// no AAO to check and creates / deactivates should always be allowed
if (signerIsApp || createOrDeactivate) {
next()
return
}
const user = recoveredSigner as Users

// fire and async update aao cache
detectAbuse(aaoConfig, user, ip).catch((e) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { Users } from '@pedalboard/storage'
import { DeveloperApps, Users } from '@pedalboard/storage'
import { ValidatedRelayRequest } from '../types/relay'

/** Context built by the context injector that can be referenced later in the request (via response object). */
export interface RequestContext {
startTime: Date
recoveredSigner: Users
ip: string
requestId: string
validatedRelayRequest: ValidatedRelayRequest

// using these booleans is clearer than
// writing a js property validator
recoveredSigner: Users | DeveloperApps
signerIsApp: boolean
signerIsUser: boolean
createOrDeactivate: boolean
}

declare global {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const incomingRequestLogger = (
next: NextFunction
) => {
const startTime = new Date(new Date().getTime());
const requestId = uuidv4();
const requestId = typeof request.headers["X-Request-Id"] === "string" ? request.headers["X-Request-ID"] as string : uuidv4();
const oldCtx = response.locals.ctx;
response.locals.ctx = { ...oldCtx, startTime, requestId };

Expand All @@ -30,7 +30,7 @@ export const outgoingLog = (request: Request, response: Response) => {
const path: string = route.path
if (!path.includes("health")) {
logger.info(
{ route, method, ctx, responseTime, statusCode },
{ route, method, abi: ctx.ctx.validatedRelayRequest.encodedABI, responseTime, statusCode },
"request completed"
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { RelayRateLimiter, ValidLimits } from '../config/rateLimitConfig'
import { Knex } from 'knex'
import { AudiusABIDecoder } from '@audius/sdk'
import { RateLimiterRes } from 'rate-limiter-flexible'
import { Table, Users } from '@pedalboard/storage'
import { config, discoveryDb } from '..'
import { DeveloperApps, Table, Users } from '@pedalboard/storage'
import { config } from '..'
import { NextFunction, Request, Response, response } from 'express'
import { rateLimitError } from '../error'
import { isUserCreate } from '../utils'

const globalRateLimiter = new RelayRateLimiter()

Expand All @@ -15,11 +16,19 @@ export const rateLimiterMiddleware = async (
res: Response,
next: NextFunction
) => {
const { validatedRelayRequest, recoveredSigner } = res.locals.ctx
const { validatedRelayRequest, recoveredSigner, signerIsUser, createOrDeactivate } = res.locals.ctx
const { encodedABI } = validatedRelayRequest

const signer = recoveredSigner.wallet
if (signer === undefined || signer === null) {
logger.info("in rate limiter!")

let signer: string | null
if (signerIsUser) {
signer = (recoveredSigner as Users).wallet!
} else {
signer = (recoveredSigner as DeveloperApps).address
}

if ((signer === undefined || signer === null) && !createOrDeactivate) {
rateLimitError(next, 'user record does not have wallet')
return
}
Expand All @@ -41,11 +50,11 @@ export const rateLimiterMiddleware = async (
}

const limit = await determineLimit(
recoveredSigner,
signerIsUser,
createOrDeactivate,
config.rateLimitAllowList,
signer
)
logger.info({ limit })

try {
const res = await globalRateLimiter.consume({
Expand Down Expand Up @@ -86,13 +95,14 @@ const insertReplyHeaders = (res: Response, data: RateLimiterRes) => {
}

const determineLimit = async (
user: Users,
isUser: boolean,
createOrDeactivate: boolean,
allowList: string[],
signer: string
): Promise<ValidLimits> => {
if (createOrDeactivate) return "app"
const isAllowed = allowList.includes(signer)
if (isAllowed) return 'allowlist'
logger.info({ user, signer })
if (user !== undefined) return 'owner'
if (isUser) return 'owner'
return 'app'
}
Loading

0 comments on commit 7af96f0

Please sign in to comment.