Skip to content

Commit

Permalink
Merge pull request #356 from Bitcoin-com/stage
Browse files Browse the repository at this point in the history
v3.2.2
  • Loading branch information
cgcardona authored Mar 19, 2019
2 parents e2bb847 + 93761cd commit f3a0af3
Show file tree
Hide file tree
Showing 16 changed files with 2,333 additions and 904 deletions.
4 changes: 4 additions & 0 deletions dist/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var helmet = require("helmet");
var debug = require("debug")("rest-cloud:server");
var http = require("http");
var cors = require("cors");
var AuthMW = require("./middleware/auth");
var BitcoinCashZMQDecoder = require("bitcoincash-zmq-decoder");
var zmq = require("zeromq");
var sock = zmq.socket("sub");
Expand Down Expand Up @@ -91,6 +92,9 @@ app.use("/" + v1prefix + "/" + "util", utilV1);
app.use("/" + v1prefix + "/" + "dataRetrieval", dataRetrievalV1);
app.use("/" + v1prefix + "/" + "payloadCreation", payloadCreationV1);
app.use("/" + v1prefix + "/" + "slp", slpV1);
// Instantiate the authorization middleware, used to implement pro-tier rate limiting.
var auth = new AuthMW();
app.use("/" + v2prefix + "/", auth.mw());
// Rate limit on all v2 routes
app.use("/" + v2prefix + "/", route_ratelimit_1.routeRateLimit);
app.use("/", indexV2);
Expand Down
69 changes: 69 additions & 0 deletions dist/middleware/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
Handle authorization for bypassing rate limits.
This file uses the passport npm library to check the header of each REST API
call for the prescence of a Basic authorization header:
https://en.wikipedia.org/wiki/Basic_access_authentication
If the header is found and validated, the req.locals.proLimit Boolean value
is set and passed to the route-ratelimits.ts middleware.
*/
"use strict";
var passport = require("passport");
var BasicStrategy = require("passport-http").BasicStrategy;
var AnonymousStrategy = require("passport-anonymous");
// Used for debugging and iterrogating JS objects.
var util = require("util");
util.inspect.defaultOptions = { depth: 1 };
var _this;
// Set default rate limit value for testing
var PRO_PASS = process.env.PRO_PASS
? parseInt(process.env.PRO_PASS)
: "BITBOX";
// Auth Middleware
var AuthMW = /** @class */ (function () {
function AuthMW() {
_this = this;
// Initialize passport for 'anonymous' authentication.
/*
passport.use(
new AnonymousStrategy({ passReqToCallback: true }, function(
req,
username,
password,
done
) {
console.log(`anonymous auth handler triggered.`)
})
)
*/
passport.use(new AnonymousStrategy());
// Initialize passport for 'basic' authentication.
passport.use(new BasicStrategy({ passReqToCallback: true }, function (req, username, password, done) {
//console.log(`req: ${util.inspect(req)}`)
//console.log(`username: ${username}`)
//console.log(`password: ${password}`)
// Create the req.locals property if it does not yet exist.
if (!req.locals)
req.locals = {};
// Evaluate the username and password and set the rate limit accordingly.
if (username === "BITBOX" && password === PRO_PASS) {
// Success
req.locals.proLimit = true;
}
else {
req.locals.proLimit = false;
}
//console.log(`req.locals: ${util.inspect(req.locals)}`)
return done(null, true);
}));
}
// Middleware called by the route.
AuthMW.prototype.mw = function () {
return passport.authenticate(["basic", "anonymous"], {
session: false
});
};
return AuthMW;
}());
module.exports = AuthMW;
83 changes: 64 additions & 19 deletions dist/middleware/route-ratelimit.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,79 @@
"use strict";
/*
This file controls the request-per-minute (RPM) rate limits.
It is assumed that this middleware is run AFTER the auth.js middleware which
checks for Basic auth. If the user adds the correct Basic auth to the header
of their API request, they will get pro-tier rate limits. By default, the
freemium rate limits apply.
*/
Object.defineProperty(exports, "__esModule", { value: true });
var RateLimit = require("express-rate-limit");
// Set max requests per minute
var maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS ? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) : 60;
var maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS
? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS)
: 60;
// Pro-tier rate limits are 10x the freemium limits.
var PRO_RPM = 10 * maxRequests;
// Unique route mapped to its rate limit
var uniqueRateLimits = {};
var routeRateLimit = function (req, res, next) {
// Create a res.locals object if not passed in.
if (!req.locals)
req.locals = {};
// Disable rate limiting if 0 passed from RATE_LIMIT_MAX_REQUESTS
if (maxRequests === 0)
return next();
// TODO: Auth: Set or disable rate limit if authenticated user
// Current route
var rateLimitTier = req.locals.proLimit ? "PRO" : "BASIC";
var path = req.baseUrl + req.path;
var route = req.method + path.split("/").slice(0, 4).join("/");
// Create new RateLimit if none exists for this route
if (!uniqueRateLimits[route]) {
uniqueRateLimits[route] = new RateLimit({
windowMs: 60 * 1000,
delayMs: 0,
max: maxRequests,
handler: function (req, res /*next*/) {
res.format({
json: function () {
res.status(500).json({
error: "Too many requests. Limits are 60 requests per minute."
});
}
});
}
});
var route = rateLimitTier +
req.method +
path
.split("/")
.slice(0, 4)
.join("/");
// This boolean value is passed from the auth.js middleware.
var proRateLimits = req.locals.proLimit;
// Pro level rate limits
if (proRateLimits) {
// TODO: replace the console.logs with calls to our logging system.
//console.log(`applying pro-rate limits`)
// Create new RateLimit if none exists for this route
if (!uniqueRateLimits[route]) {
uniqueRateLimits[route] = new RateLimit({
windowMs: 60 * 1000,
delayMs: 0,
max: PRO_RPM,
handler: function (req, res /*next*/) {
//console.log(`pro-tier rate-handler triggered.`)
res.status(429); // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: "Too many requests. Limits are " + PRO_RPM + " requests per minute."
});
}
});
}
// Freemium level rate limits
}
else {
// TODO: replace the console.logs with calls to our logging system.
//console.log(`applying freemium limits`)
// Create new RateLimit if none exists for this route
if (!uniqueRateLimits[route]) {
uniqueRateLimits[route] = new RateLimit({
windowMs: 60 * 1000,
delayMs: 0,
max: maxRequests,
handler: function (req, res /*next*/) {
//console.log(`freemium rate-handler triggered.`)
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."
});
}
});
}
}
// Call rate limit for this route
uniqueRateLimits[route](req, res, next);
Expand Down
2 changes: 1 addition & 1 deletion dist/public/bitcoin-com-mainnet-rest-v2.json
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,7 @@
"openapi": "3.0.0",
"info": {
"description": "rest.bitcoin.com is the REST layer for Bitcoin.com's Cloud. More info: [developer.bitcoin.com/rest](https://developer.bitcoin.com/rest). Chatroom [geni.us/CashDev](http://geni.us/CashDev)",
"version": "3.2.1",
"version": "3.2.2",
"title": "REST",
"license": {
"name": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion dist/public/bitcoin-com-testnet-rest-v2.json
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,7 @@
"openapi": "3.0.0",
"info": {
"description": "trest.bitcoin.com is the REST layer for Bitcoin.com's Cloud. More info: [developer.bitcoin.com/rest](https://developer.bitcoin.com/rest). Chatroom [geni.us/CashDev](http://geni.us/CashDev)",
"version": "3.2.1",
"version": "3.2.2",
"title": "REST",
"license": {
"name": "MIT",
Expand Down
Loading

0 comments on commit f3a0af3

Please sign in to comment.