Add rate limiting and tidy up various code.
Will need complete review long before merging to main. Signed-off-by: Fred Boniface <fred@fjla.uk>
This commit is contained in:
parent
3f7defcf8e
commit
096ce154da
9
app.js
9
app.js
@ -12,6 +12,7 @@ const app = express();
|
|||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
const compression = require('compression')
|
const compression = require('compression')
|
||||||
|
const rateLimit = require('express-rate-limit')
|
||||||
const authenticate= require('./src/middlewares/auth.middlewares')
|
const authenticate= require('./src/middlewares/auth.middlewares')
|
||||||
|
|
||||||
// Internal Requires
|
// Internal Requires
|
||||||
@ -29,6 +30,13 @@ const regRtr = require('./src/routes/registration.routes'); // /auth end
|
|||||||
const srvListen = process.env.OWL_SRV_LISTEN || "0.0.0.0"
|
const srvListen = process.env.OWL_SRV_LISTEN || "0.0.0.0"
|
||||||
const srvPort = process.env.OWL_SRV_PORT || 8460
|
const srvPort = process.env.OWL_SRV_PORT || 8460
|
||||||
|
|
||||||
|
const limiter = rateLimit({
|
||||||
|
windowMs: 15 * (60 * 1000), // 15 minutes
|
||||||
|
max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
|
||||||
|
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
||||||
|
legacyHeaders: true, // Disable the `X-RateLimit-*` headers
|
||||||
|
})
|
||||||
|
|
||||||
// Print version number:
|
// Print version number:
|
||||||
log.out(`app: Starting OwlBoard - Backend Version: ${version.app} - API versions: ${version.api}`, "init");
|
log.out(`app: Starting OwlBoard - Backend Version: ${version.app} - API versions: ${version.api}`, "init");
|
||||||
|
|
||||||
@ -52,6 +60,7 @@ app.use((err, req, res, next) => {
|
|||||||
// Global Middleware:
|
// Global Middleware:
|
||||||
app.use(express.json()); //JSON Parsing for POST Requests
|
app.use(express.json()); //JSON Parsing for POST Requests
|
||||||
app.use(compression()) // Compress API Data if supported by client
|
app.use(compression()) // Compress API Data if supported by client
|
||||||
|
app.use(limiter)
|
||||||
|
|
||||||
// Unauthenticated Routes
|
// Unauthenticated Routes
|
||||||
app.use('/api/v1/list', listRtr);
|
app.use('/api/v1/list', listRtr);
|
||||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@ -12,6 +12,7 @@
|
|||||||
"axios": "^1.2.1",
|
"axios": "^1.2.1",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"express-rate-limit": "^6.7.0",
|
||||||
"ldbs-json": "^1.2.1",
|
"ldbs-json": "^1.2.1",
|
||||||
"mongodb": "^4.13.0",
|
"mongodb": "^4.13.0",
|
||||||
"nodemailer": "^6.9.1",
|
"nodemailer": "^6.9.1",
|
||||||
@ -1426,6 +1427,17 @@
|
|||||||
"node": ">= 0.10.0"
|
"node": ">= 0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/express-rate-limit": {
|
||||||
|
"version": "6.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz",
|
||||||
|
"integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"express": "^4 || ^5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/express/node_modules/safe-buffer": {
|
"node_modules/express/node_modules/safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
"axios": "^1.2.1",
|
"axios": "^1.2.1",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"express-rate-limit": "^6.7.0",
|
||||||
"ldbs-json": "^1.2.1",
|
"ldbs-json": "^1.2.1",
|
||||||
"mongodb": "^4.13.0",
|
"mongodb": "^4.13.0",
|
||||||
"nodemailer": "^6.9.1",
|
"nodemailer": "^6.9.1",
|
||||||
|
@ -11,7 +11,7 @@ module.exports = async function authCheck(req, res, next) {
|
|||||||
return next(err)
|
return next(err)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var result = await utils.isAuthed(uuid) | false
|
var result = await utils.isAuthed(uuid) || false
|
||||||
if (!result) {
|
if (!result) {
|
||||||
const err = new Error("Unauthorised");
|
const err = new Error("Unauthorised");
|
||||||
err.status = 401
|
err.status = 401
|
||||||
|
@ -23,7 +23,7 @@ async function send(message){ // message is an object containing strings for: *t
|
|||||||
try {
|
try {
|
||||||
var res = await transporter.sendMail(message)
|
var res = await transporter.sendMail(message)
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
log.out(`mailServices.send: Message send failed`, "err")
|
log.out(`mailServices.send: Message send failed: ${err}`, "err")
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
log.out(`mailServices.send: SMTP Response: ${res.response}`)
|
log.out(`mailServices.send: SMTP Response: ${res.response}`)
|
||||||
|
@ -7,15 +7,14 @@ const domains = require('../configs/domains.configs')
|
|||||||
|
|
||||||
async function createRegKey(body) {
|
async function createRegKey(body) {
|
||||||
log.out(`registerServices.createRegKey: Incoming request`, "INFO")
|
log.out(`registerServices.createRegKey: Incoming request`, "INFO")
|
||||||
log.out(`registerServices.createRegKey: ${JSON.stringify(domains)}`)
|
const domain = await clean.getDomainFromEmail(body.email) // The function should validate the email
|
||||||
const domain = await clean.splitDomain(body.email)
|
|
||||||
log.out(`registerServices: New registration request from domain: ${domain}`, "info")
|
log.out(`registerServices: New registration request from domain: ${domain}`, "info")
|
||||||
if (domains.includes(domain)) { // Needs testing
|
if (domains.includes(domain)) {
|
||||||
log.out(`registerServices.createRegKey: Key from valid domain: ${domain}`)
|
log.out(`registerServices.createRegKey: Key from valid domain: ${domain}`)
|
||||||
const uuid = auth.generateKey();
|
const uuid = await auth.generateKey();
|
||||||
db.addRegReq(await uuid, domain)
|
db.addRegReq(uuid, domain)
|
||||||
const message = auth.generateConfirmationEmail(body.email, uuid)
|
const message = await auth.generateConfirmationEmail(body.email, uuid)
|
||||||
if (await message == false) { // This error should be handled in the upstream function
|
if (!message) {
|
||||||
const err = new Error("Message generation error");
|
const err = new Error("Message generation error");
|
||||||
log.out(`registerServices.createRegKey: Error generating registration email`, "err")
|
log.out(`registerServices.createRegKey: Error generating registration email`, "err")
|
||||||
return 500;
|
return 500;
|
||||||
@ -28,19 +27,19 @@ async function createRegKey(body){
|
|||||||
return {status: 403, message: "forbidden, domain is not on whitelist"}
|
return {status: 403, message: "forbidden, domain is not on whitelist"}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function regUser(req) {
|
async function regUser(req) { // Add input validation
|
||||||
log.out(`registrationServices.regUser: Checking validity of : ${req.uuid}`)
|
log.out(`registrationServices.regUser: Checking validity of : ${req.uuid}`, "info")
|
||||||
let res = await auth.checkRequest(req.uuid)
|
const res = await auth.checkRequest(req.uuid)
|
||||||
log.out(`registrationServices.regUser: checkRequest returned: ${JSON.stringify(res)}`)
|
log.out(`registrationServices.regUser: checkRequest returned: ${JSON.stringify(res)}`, "info")
|
||||||
if (res.result) {
|
if (res.result) {
|
||||||
let uuid = await auth.generateKey()
|
const uuid = await auth.generateKey()
|
||||||
let apiKey = await db.addUser(uuid, res.domain)
|
const apiKey = await db.addUser(uuid, res.domain)
|
||||||
if (apiKey) {
|
if (apiKey) {
|
||||||
db.delRegReq(req.uuid)
|
db.delRegReq(req.uuid)
|
||||||
return {status: 201, message: "User added", api_key: uuid}
|
return {status: 201, message: "User added", api_key: uuid}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {status: 401, message: "Unautorised"}
|
return {status: 401, message: "Unauthorised"}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -5,27 +5,24 @@ const fs = require('fs/promises')
|
|||||||
|
|
||||||
// Checks users registration key against issued keys
|
// Checks users registration key against issued keys
|
||||||
async function isAuthed(uuid) { // Needs testing
|
async function isAuthed(uuid) { // Needs testing
|
||||||
q = {uuid: uuid};
|
const q = {uuid: uuid}
|
||||||
res = await db.query("users", q);
|
const res = await db.query("users", q)
|
||||||
log.out(`authUtils.checkUser: DB Query answer: ${JSON.stringify(res[0])}`)
|
log.out(`authUtils.checkUser: DB Query answer: ${JSON.stringify(res[0])}`, "dbug")
|
||||||
// Do something here to determine if authorised or not and simply return a BOOL
|
const authorized = res && res[0] && res[0].domain;
|
||||||
if (res[0].domain) {
|
if (authorized) db.userAtime(uuid)
|
||||||
db.userAtime(uuid)
|
return authorized
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks whether a registration request key is valid
|
// Checks whether a registration request key is valid
|
||||||
async function checkRequest(key) {
|
async function checkRequest(key) {
|
||||||
let collection = "registrations"
|
const collection = "registrations"
|
||||||
let query = {uuid: key}
|
const query = {uuid: key}
|
||||||
const res = await db.query(collection, query)
|
const res = await db.query(collection, query)
|
||||||
log.out(`authUtils.checkRequest: DB Query result: ${JSON.stringify(res)}`, "info")
|
log.out(`authUtils.checkRequest: DB Query result: ${JSON.stringify(res)}`, "dbug")
|
||||||
if (res[0].time) {
|
const result = res.length > 0 && res[0].time
|
||||||
return {result: true, domain: res[0].domain}
|
? { result: true, domain: res[0].domain }
|
||||||
}
|
: { result: false };
|
||||||
return {result: false}
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates an API key for a user
|
// Creates an API key for a user
|
||||||
|
@ -4,10 +4,10 @@ const san = require('../utils/sanitizer.utils') // Sanitiser
|
|||||||
|
|
||||||
async function checkCrs(input){
|
async function checkCrs(input){
|
||||||
var INPUT = input.toUpperCase()
|
var INPUT = input.toUpperCase()
|
||||||
log.out(`ldbUtils.checkCrs: Building database query to find: '${INPUT}'`, "info")
|
log.out(`ldbUtils.checkCrs: Building database query to find: '${INPUT}'`, "dbug")
|
||||||
var query = {'$or':[{'3ALPHA':INPUT},{'TIPLOC':INPUT},{'STANOX':INPUT}]};
|
var query = {'$or':[{'3ALPHA':INPUT},{'TIPLOC':INPUT},{'STANOX':INPUT}]};
|
||||||
var result = await db.query("stations", query)
|
var result = await db.query("stations", query)
|
||||||
log.out(`ldbUtils.checkCrs: Query results: ${JSON.stringify(result)}`, "info")
|
log.out(`ldbUtils.checkCrs: Query results: ${JSON.stringify(result)}`, "dbug")
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
const environment = process.env.NODE_ENV;
|
const environment = process.env.NODE_ENV;
|
||||||
|
|
||||||
|
const hideInProduction = ["info", "dbug"]
|
||||||
|
|
||||||
async function out(msg, level = 'othr') {
|
async function out(msg, level = 'othr') {
|
||||||
if (environment === "production" && level === "info") {
|
if (environment === "production" && hideInProduction.includes(level)) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
const time = new Date().toISOString();
|
const time = new Date().toISOString();
|
||||||
|
@ -38,7 +38,7 @@ function cleanNrcc(input) {
|
|||||||
return rmPara;
|
return rmPara;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function splitDomain(mail) { // Needs testing
|
async function getDomainFromEmail(mail) { // Needs testing
|
||||||
split = mail.split("@")
|
split = mail.split("@")
|
||||||
return split[1]
|
return split[1]
|
||||||
}
|
}
|
||||||
@ -47,5 +47,5 @@ module.exports = {
|
|||||||
cleanApiEndpointTxt,
|
cleanApiEndpointTxt,
|
||||||
cleanApiEndpointNum,
|
cleanApiEndpointNum,
|
||||||
cleanNrcc,
|
cleanNrcc,
|
||||||
splitDomain
|
getDomainFromEmail
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user