diff --git a/src/app.js b/src/app.js index 7f04668..20b90bc 100644 --- a/src/app.js +++ b/src/app.js @@ -76,7 +76,7 @@ app.use(`/${v3prefix}/`, auth.mw()) // Rate limit on all v3 routes app.use(`/${v3prefix}/`, routeRateLimit) // Establish and enforce rate limits. -app.use(`/${v3prefix}/`, jwtAuth.routeAccess) // Enforce access tiers. +// app.use(`/${v3prefix}/`, jwtAuth.routeAccess) // Enforce access tiers. app.use(`/${v3prefix}/` + `health-check`, healthCheckV3) app.use(`/${v3prefix}/` + `blockchain`, blockchainV3.router) app.use(`/${v3prefix}/` + `control`, controlV3.router) diff --git a/src/middleware/jwt-auth.js b/src/middleware/jwt-auth.js index 684f95a..be719fc 100644 --- a/src/middleware/jwt-auth.js +++ b/src/middleware/jwt-auth.js @@ -60,8 +60,8 @@ const routeAccess = (req, res, next) => { // API Level is 0 (free tier): do nothing. All endpoints are open to // free public access within the drastically limited rate limits. - if (locals.apiLevel === 10) { - // API level is 10 (full node tier) + if (locals.apiLevel < 20) { + // API level does not include indexer access. // Loop through the routes that are not accessible to this tier. for (let i = 0; i < level20Routes.length; i++) { diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 2bb2c2b..fca640d 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -53,17 +53,35 @@ const routeRateLimit = async function(req, res, next) { if (req.locals.jwtToken) { // console.log(`req.locals.jwtToken: ${req.locals.jwtToken}`) + // URL for the auth server. const path = `${authServer}apitoken/isvalid/${req.locals.jwtToken}` + // Ask Auth server if the JWT token is valid. + // Get the API level for this user. let jwtInfo = await axios.get(path) jwtInfo = jwtInfo.data - // console.log(`jwtInfo: ${JSON.stringify(jwtInfo, null, 2)}`) + console.log(`jwtInfo: ${JSON.stringify(jwtInfo, null, 2)}`) - // Enable pro-tier rate limits if JWT if valid. + // If JWT if valid, evaluate the API level for the user. if (jwtInfo.isValid) { - // console.log(`JWT is valid. Enabling pro-tier rate limits.`) - req.locals.proLimit = true - req.locals.apiLevel = jwtInfo.apiLevel + // Set fine-grain permissions for each user based on the JWT token. + const userPermissions = evalUserPermissioins(req, jwtInfo) + console.log( + `userPermissions: ${JSON.stringify(userPermissions, null, 2)}` + ) + console.log(` `) + + req.locals.proLimit = userPermissions.proLimit + req.locals.apiLevel = userPermissions.apiLevel + + // const locals = req.locals + // console.log(`locals: ${JSON.stringify(locals, null, 2)}`) + // const url = req.url + // console.log(`url: ${JSON.stringify(url, null, 2)}`) + // + // // console.log(`JWT is valid. Enabling pro-tier rate limits.`) + // req.locals.proLimit = true + // req.locals.apiLevel = jwtInfo.apiLevel } } } @@ -126,7 +144,7 @@ const routeRateLimit = async function(req, res, next) { res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 return res.json({ - error: `Too many requests. Limits are ${maxRequests} requests per minute.` + error: `Too many requests. Your limits are currently ${maxRequests} requests per minute.` }) } }) @@ -139,4 +157,39 @@ const routeRateLimit = async function(req, res, next) { uniqueRateLimits[route](req, res, next) } +// This function returns the an object with proLimit and apiLevel properties. +// It does fine-grane analysis on the data coming from the auth servers and +// uses its output to adjust rate limits on-the-fly based on the users +// permission level. +function evalUserPermissioins(req, authData) { + console.log(`authData: ${JSON.stringify(authData, null, 2)}`) + + // Return object with default values + const retObj = { + proLimit: authData.isValid, + apiLevel: authData.apiLevel + } + + const level20Routes = ["insight", "bitcore", "blockbook"] + + const locals = req.locals + console.log(`locals: ${JSON.stringify(locals, null, 2)}`) + const url = req.url + console.log(`url: ${JSON.stringify(url, null, 2)}`) + + if (authData.apiLevel < 20) { + // Loop through the routes that are not accessible to this tier. + for (let i = 0; i < level20Routes.length; i++) { + // If the requested route is for a higher tier, + // revert to anonymous level permissions. + if (url.indexOf(level20Routes[i]) > -1) { + retObj.proLimit = false + retObj.apiLevel = 0 + } + } + } + + return retObj +} + module.exports = { routeRateLimit }