Got rough workflow sketched out for new rate limit handler

This commit is contained in:
Chris Troutner
2021-03-07 11:25:50 -08:00
parent bb3d020646
commit bcfe503aa6
+101 -11
View File
@@ -11,7 +11,7 @@ The rate limit handling is designed for these four use cases:
- Users who want to buy different RPM tiers: 100, 250, 600 - Users who want to buy different RPM tiers: 100, 250, 600
- Basic Authentication which should not have any rate limits applied. - Basic Authentication which should not have any rate limits applied.
The Basci Auth use cases is considered when determining internal rate limits. The Basic Auth use cases is considered when determining internal rate limits.
The internal rate limits should not be applied to calls from those users. The internal rate limits should not be applied to calls from those users.
A lot of attention has been paid to passing rate-limit information for the user A lot of attention has been paid to passing rate-limit information for the user
@@ -25,10 +25,10 @@ const Redis = require('ioredis')
const { RateLimiterRedis } = require('rate-limiter-flexible') const { RateLimiterRedis } = require('rate-limiter-flexible')
// local libraries. // local libraries.
// const wlogger = require('../util/winston-logging') const wlogger = require('../util/winston-logging')
const config = require('../../config') const config = require('../../config')
// let _this // Global pointer to instance of class, when 'this' context is lost. let _this // Global pointer to instance of class, when 'this' context is lost.
// Setup Redis to track rate limits for each user. // Setup Redis to track rate limits for each user.
const redisOptions = { const redisOptions = {
@@ -45,14 +45,14 @@ const rateLimitOptions = {
} }
// Constants // Constants
// const ANON_LIMITS = config.anonRateLimit const ANON_LIMITS = config.anonRateLimit
// const WHITELIST_RATE_LIMIT = config.whitelistRateLimit const WHITELIST_RATE_LIMIT = config.whitelistRateLimit
// const WHITELIST_DOMAINS = config.whitelistDomains const WHITELIST_DOMAINS = config.whitelistDomains
// const INTERNAL_RATE_LIMIT = 1 const INTERNAL_RATE_LIMIT = 1
class RateLimits { class RateLimits {
constructor () { constructor () {
// _this = this _this = this
this.jwt = jwt this.jwt = jwt
this.rateLimiter = new RateLimiterRedis(rateLimitOptions) this.rateLimiter = new RateLimiterRedis(rateLimitOptions)
@@ -63,8 +63,8 @@ class RateLimits {
// support this function. // support this function.
async applyRateLimits (req, res, next) { async applyRateLimits (req, res, next) {
try { try {
// let userId let userId
// let decoded = {} const decoded = {}
// Create a re*Q*.locals object if not passed in. // Create a re*Q*.locals object if not passed in.
// req.locals.proLimit will be true if the user is using Basic Authentication. // req.locals.proLimit will be true if the user is using Basic Authentication.
@@ -83,8 +83,98 @@ class RateLimits {
rateLimitTriggered: false rateLimitTriggered: false
} }
} }
} catch (err) {
// Exit if the user has already authenticated with Basic Authentication.
if (req.locals.proLimit) {
return next()
}
// Determine if the call is an external or internal API call.
const isInternal = this.checkInternalIp(req)
// Determine if the call originates from another computer on the intranet.
const isWhitelistOrigin = this.isInWhitelist(req)
console.log('isWhitelistOrigin: ', isWhitelistOrigin)
// Handle the use case of internally-generated requests.
if (isInternal) {
// Internal API calls should pass the authentication data in through the
// the usrObj in the body.
if (req.body && req.body.usrObj) {
//
if (req.body.usrObj.proLimit) {
// If this is an internal call that originated from a user using
// Basic Authentication, then skip rate-limits.
return next()
//
} else if (req.body.usrObj.jwtToken) {
// Internal call originated from a user using a JWT token.
console.log(
'Internal call originated from a user using a JWT token'
)
//
} else {
// Internal call originates from an anonymous user.
console.log('Internal call originates from an anonymous user')
}
}
}
} catch (err) {
wlogger.error('Error in route-ratelimit2.js/applyRateLimits(): ', err)
}
next()
}
// Returns a boolean if the origin of the request matches a domain in the
// whitelist.
isInWhitelist (req) {
try {
const retVal = false // Default value.
// Retrieve the origin.
const origin = req.get('origin')
// console.log(`WHITELIST_DOMAINS: ${JSON.stringify(WHITELIST_DOMAINS, null, 2)}`)
for (let i = 0; i < WHITELIST_DOMAINS.length; i++) {
const thisDomain = WHITELIST_DOMAINS[i]
if (origin.toString().indexOf(thisDomain) > -1) {
return true
}
}
return retVal
} catch (err) {
wlogger.error(
'Error in route-ratelimit.js/isInWhitelist(). Returning false by default.'
)
return false
}
}
// Checks the request object to see if it's IP address matches an internal
// IP address. That means the call is an internal API call and should be
// treated differently than an external API call.
checkInternalIp (req) {
try {
// Default value
let isInternal = false
const ip = req.ip
if (ip.includes('127.0.0.1')) isInternal = true
if (ip.includes('172.17.')) isInternal = true
return isInternal
} catch (err) {
console.error(
'Error in checkInternalIp(). Returning false be default. Err: ',
err
)
return false
} }
} }