Skip to content

Commit

Permalink
Merge pull request #400 from bluewave-labs/397-create-ip-check
Browse files Browse the repository at this point in the history
397 create ip check
  • Loading branch information
swoopertr authored Dec 19, 2024
2 parents 6c2f98b + f1cf159 commit 7448e08
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 36 deletions.
5 changes: 5 additions & 0 deletions backend/.env
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@ TEST_DB_PASSWORD=password123
TEST_DB_NAME=onboarding_db_test
TEST_DB_HOST=localhost
TEST_DB_PORT=5432

# Allowed IP range for the API "baseIp/rangeStart-rangeEnd" (e.g. 192.168.1/1-255) separated by comma
ALLOWED_IP_RANGE=11.22.33/10-200, 192.168.65/1-255
# Allowed IP addresses for the API separated by comma
ALLOWED_IPS=127.0.0.1, 11.22.33.44, 11.22.33.45, 11.22.33.46, 192.168.65.1
5 changes: 5 additions & 0 deletions backend/.env.production
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ PROD_DB_PORT=5432

# JWT Secret Key
JWT_SECRET=your_prod_jwt_secret_key_here

# Allowed IP range for the API "baseIp/rangeStart-rangeEnd" (e.g. 192.168.1/1-255) separated by comma
ALLOWED_IP_RANGE=11.22.33/10-200
# Allowed IP addresses for the API separated by comma
ALLOWED_IPS=11.22.33.44, 11.22.33.45, 11.22.33.46
5 changes: 5 additions & 0 deletions backend/.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ POSTGRES_DB=onboarding_db_test

# JWT Secret Key
JWT_SECRET=your_test_jwt_secret_key_here

# Allowed IP range for the API "baseIp/rangeStart-rangeEnd" (e.g. 192.168.1/1-255) separated by comma
ALLOWED_IP_RANGE=11.22.33/10-200, 192.168.65/1-255
# Allowed IP addresses for the API separated by comma
ALLOWED_IPS=127.0.0.1, 11.22.33.44, 11.22.33.45, 11.22.33.46, 192.168.65.1
69 changes: 69 additions & 0 deletions backend/src/middleware/ipFilter.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const getIpFromRequest = (req) => {
const forwardedFor = req.headers["x-forwarded-for"];
const remoteAddress = req.connection.remoteAddress;
return (forwardedFor || remoteAddress).replace("::ffff:", "");
};

const parseIpRange = (allowedIpsEnv) => {
if (!allowedIpsEnv) return [];

const ranges = allowedIpsEnv.split(", ").map((range) => range.trim());
return ranges.map((range) => {
const [baseIp, endRange] = range.split("/");
if (!baseIp || !endRange) throw new Error("Invalid IP range format");
const rangeParts = endRange.split("-");
return {
baseIp,
rangeStart: parseInt(rangeParts[0]),
rangeEnd: parseInt(rangeParts[1]),
};
});
};

const isIpAllowed = (currentIp, allowedRanges) => {
const ipParts = currentIp.split(".");
const startIp = ipParts.slice(0, 3).join(".");
const lastOctet = parseInt(ipParts[3], 10);

for (const range of allowedRanges) {
if (
startIp === range.baseIp &&
lastOctet >= range.rangeStart &&
lastOctet <= range.rangeEnd
) {
return true;
}
}
return false;
};

const ipFilter = async (req, res, next) => {
try {
const currentIp = getIpFromRequest(req);
const allowedIpsEnv = process.env.ALLOWED_IPS;
const allowedRanges = parseIpRange(process.env.ALLOWED_IP_RANGE);
if (!allowedIpsEnv && !allowedRanges.length) {
next();
return;
} else if (!allowedIpsEnv) {
if (!isIpAllowed(currentIp, allowedRanges)) {
return res.status(401).json({ error: "Unauthorized" });
}
next();
return;
}

const allowedIps = allowedIpsEnv.split(", ").map((ip) => ip.trim());

if (!allowedIps.includes(currentIp)) {
return res.status(401).json({ error: "Unauthorized" });
}

next();
} catch (err) {
console.error("IP Filter Error:", err.message);
return res.status(500).json({ error: "Internal Server Error" });
}
};

module.exports = ipFilter;
74 changes: 38 additions & 36 deletions backend/src/server.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,66 @@
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const dotenv = require('dotenv');
const bodyParser = require('body-parser');
const jsonErrorMiddleware = require('./middleware/jsonError.middleware');
const fileSizeValidator = require('./middleware/fileSizeValidator.middleware');
const { MAX_FILE_SIZE } = require('./utils/constants.helper');
const express = require("express");
const cors = require("cors");
const helmet = require("helmet");
const dotenv = require("dotenv");
const bodyParser = require("body-parser");
const jsonErrorMiddleware = require("./middleware/jsonError.middleware");
const fileSizeValidator = require("./middleware/fileSizeValidator.middleware");
const { MAX_FILE_SIZE } = require("./utils/constants.helper");
const ipFilter = require("./middleware/ipFilter.middleware");

// Load environment variables from .env file
dotenv.config();

const authRoutes = require('./routes/auth.routes');
const userRoutes = require('./routes/user.routes');
const mocks = require('./routes/mocks.routes');
const popup = require('./routes/popup.routes');
const guide_log = require('./routes/guidelog.routes');
const banner = require('./routes/banner.routes');
const teamRoutes = require('./routes/team.routes');
const hint = require('./routes/hint.routes');
const tourRoutes = require('./routes/tour.routes');
const linkRoutes = require('./routes/link.routes');
const helperLinkRoutes = require('./routes/helperLink.routes');
const guideRoutes = require('./routes/guide.routes');
const authRoutes = require("./routes/auth.routes");
const userRoutes = require("./routes/user.routes");
const mocks = require("./routes/mocks.routes");
const popup = require("./routes/popup.routes");
const guide_log = require("./routes/guidelog.routes");
const banner = require("./routes/banner.routes");
const teamRoutes = require("./routes/team.routes");
const hint = require("./routes/hint.routes");
const tourRoutes = require("./routes/tour.routes");
const linkRoutes = require("./routes/link.routes");
const helperLinkRoutes = require("./routes/helperLink.routes");
const guideRoutes = require("./routes/guide.routes");

const app = express();

app.use(cors());
app.use(helmet());
app.use(bodyParser.json({ limit: MAX_FILE_SIZE }));
app.use(jsonErrorMiddleware);
app.use(ipFilter);
// app.use(fileSizeValidator);

const { sequelize } = require("./models");

sequelize
.authenticate()
.then(() => console.log('Database connected...'))
.catch((err) => console.log('Error: ' + err));
.then(() => console.log("Database connected..."))
.catch((err) => console.log("Error: " + err));

sequelize
.sync({ force: false })
.then(() => console.log("Models synced with the database..."))
.catch((err) => console.log("Error syncing models: " + err));

app.use('/api/auth', authRoutes);
app.use('/api/users', userRoutes);
app.use('/api/mock/', mocks);
app.use('/api/popup', popup);
app.use('/api/guide_log', guide_log);
app.use('/api/banner', banner);
app.use('/api/team', teamRoutes);
app.use('/api/guide', guideRoutes);
app.use('/api/hint', hint);
app.use('/api/tour', tourRoutes);
app.use('/api/link', linkRoutes);
app.use('/api/helper-link', helperLinkRoutes);
app.use("/api/auth", authRoutes);
app.use("/api/users", userRoutes);
app.use("/api/mock/", mocks);
app.use("/api/popup", popup);
app.use("/api/guide_log", guide_log);
app.use("/api/banner", banner);
app.use("/api/team", teamRoutes);
app.use("/api/guide", guideRoutes);
app.use("/api/hint", hint);
app.use("/api/tour", tourRoutes);
app.use("/api/link", linkRoutes);
app.use("/api/helper-link", helperLinkRoutes);

app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ message: 'Internal Server Error' });
res.status(500).json({ message: "Internal Server Error" });
});

module.exports = app;
module.exports = app;

0 comments on commit 7448e08

Please sign in to comment.