🔒 feat: Authenticated Image Requests (#2389)

* 🔒 feat: Authenticated Image Requests

* fix: reserved keyword `static`
This commit is contained in:
Danny Avila 2024-04-11 02:50:57 -04:00 committed by GitHub
parent c19dfddd0f
commit 9277e2a0c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 59 additions and 6 deletions

View file

@ -6,6 +6,8 @@ module.exports = {
clientPath: path.resolve(__dirname, '..', '..', 'client'), clientPath: path.resolve(__dirname, '..', '..', 'client'),
dist: path.resolve(__dirname, '..', '..', 'client', 'dist'), dist: path.resolve(__dirname, '..', '..', 'client', 'dist'),
publicPath: path.resolve(__dirname, '..', '..', 'client', 'public'), publicPath: path.resolve(__dirname, '..', '..', 'client', 'public'),
fonts: path.resolve(__dirname, '..', '..', 'client', 'public', 'fonts'),
assets: path.resolve(__dirname, '..', '..', 'client', 'public', 'assets'),
imageOutput: path.resolve(__dirname, '..', '..', 'client', 'public', 'images'), imageOutput: path.resolve(__dirname, '..', '..', 'client', 'public', 'images'),
structuredTools: path.resolve(__dirname, '..', 'app', 'clients', 'tools', 'structured'), structuredTools: path.resolve(__dirname, '..', 'app', 'clients', 'tools', 'structured'),
pluginManifest: path.resolve(__dirname, '..', 'app', 'clients', 'tools', 'manifest.json'), pluginManifest: path.resolve(__dirname, '..', 'app', 'clients', 'tools', 'manifest.json'),

View file

@ -76,14 +76,14 @@ const refreshController = async (req, res) => {
} }
try { try {
let payload; const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET); const user = await User.findOne({ _id: payload.id });
const userId = payload.id;
const user = await User.findOne({ _id: userId });
if (!user) { if (!user) {
return res.status(401).redirect('/login'); return res.status(401).redirect('/login');
} }
const userId = payload.id;
if (process.env.NODE_ENV === 'CI') { if (process.env.NODE_ENV === 'CI') {
const token = await setAuthTokens(userId, res); const token = await setAuthTokens(userId, res);
const userObj = user.toJSON(); const userObj = user.toJSON();
@ -118,6 +118,6 @@ module.exports = {
getUserController, getUserController,
refreshController, refreshController,
registrationController, registrationController,
resetPasswordRequestController,
resetPasswordController, resetPasswordController,
resetPasswordRequestController,
}; };

View file

@ -6,6 +6,7 @@ const axios = require('axios');
const express = require('express'); const express = require('express');
const passport = require('passport'); const passport = require('passport');
const mongoSanitize = require('express-mongo-sanitize'); const mongoSanitize = require('express-mongo-sanitize');
const validateImageRequest = require('./middleware/validateImageRequest');
const errorController = require('./controllers/ErrorController'); const errorController = require('./controllers/ErrorController');
const { jwtLogin, passportLogin } = require('~/strategies'); const { jwtLogin, passportLogin } = require('~/strategies');
const configureSocialLogins = require('./socialLogins'); const configureSocialLogins = require('./socialLogins');
@ -43,7 +44,8 @@ const startServer = async () => {
app.use(mongoSanitize()); app.use(mongoSanitize());
app.use(express.urlencoded({ extended: true, limit: '3mb' })); app.use(express.urlencoded({ extended: true, limit: '3mb' }));
app.use(express.static(app.locals.paths.dist)); app.use(express.static(app.locals.paths.dist));
app.use(express.static(app.locals.paths.publicPath)); app.use(express.static(app.locals.paths.fonts));
app.use(express.static(app.locals.paths.assets));
app.set('trust proxy', 1); // trust first proxy app.set('trust proxy', 1); // trust first proxy
app.use(cors()); app.use(cors());
@ -82,6 +84,7 @@ const startServer = async () => {
app.use('/api/config', routes.config); app.use('/api/config', routes.config);
app.use('/api/assistants', routes.assistants); app.use('/api/assistants', routes.assistants);
app.use('/api/files', await routes.files.initialize()); app.use('/api/files', await routes.files.initialize());
app.use('/images/', validateImageRequest, routes.staticRoute);
app.use((req, res) => { app.use((req, res) => {
res.status(404).sendFile(path.join(app.locals.paths.dist, 'index.html')); res.status(404).sendFile(path.join(app.locals.paths.dist, 'index.html'));

View file

@ -14,6 +14,7 @@ const concurrentLimiter = require('./concurrentLimiter');
const validateMessageReq = require('./validateMessageReq'); const validateMessageReq = require('./validateMessageReq');
const buildEndpointOption = require('./buildEndpointOption'); const buildEndpointOption = require('./buildEndpointOption');
const validateRegistration = require('./validateRegistration'); const validateRegistration = require('./validateRegistration');
const validateImageRequest = require('./validateImageRequest');
const moderateText = require('./moderateText'); const moderateText = require('./moderateText');
const noIndex = require('./noIndex'); const noIndex = require('./noIndex');
@ -33,6 +34,7 @@ module.exports = {
validateMessageReq, validateMessageReq,
buildEndpointOption, buildEndpointOption,
validateRegistration, validateRegistration,
validateImageRequest,
validateModel, validateModel,
moderateText, moderateText,
noIndex, noIndex,

View file

@ -0,0 +1,37 @@
const cookies = require('cookie');
const jwt = require('jsonwebtoken');
const { logger } = require('~/config');
/**
* Middleware to validate image request
*/
function validateImageRequest(req, res, next) {
const refreshToken = req.headers.cookie ? cookies.parse(req.headers.cookie).refreshToken : null;
if (!refreshToken) {
logger.warn('[validateImageRequest] Refresh token not provided');
return res.status(401).send('Unauthorized');
}
let payload;
try {
payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
} catch (err) {
logger.warn('[validateImageRequest]', err);
return res.status(403).send('Access Denied');
}
const currentTimeInSeconds = Math.floor(Date.now() / 1000);
if (payload.exp < currentTimeInSeconds) {
logger.warn('[validateImageRequest] Refresh token expired');
return res.status(403).send('Access Denied');
}
if (req.path.includes(payload.id)) {
logger.debug('[validateImageRequest] Image request validated');
next();
} else {
res.status(403).send('Access Denied');
}
}
module.exports = validateImageRequest;

View file

@ -17,6 +17,7 @@ const user = require('./user');
const config = require('./config'); const config = require('./config');
const assistants = require('./assistants'); const assistants = require('./assistants');
const files = require('./files'); const files = require('./files');
const staticRoute = require('./static');
module.exports = { module.exports = {
search, search,
@ -38,4 +39,5 @@ module.exports = {
config, config,
assistants, assistants,
files, files,
staticRoute,
}; };

View file

@ -0,0 +1,7 @@
const express = require('express');
const paths = require('~/config/paths');
const router = express.Router();
router.use(express.static(paths.imageOutput));
module.exports = router;