feat(validation): introduce express-validator on CMS user and article endpoints
Replaces the broken fieldValidation helper (called next() without return, so execution continued on invalid input) with express-validator chains applied at the route level. Validation covers: email format, username constraints, password min length, field lengths, URL and phone formats. products and tags controllers retain their local fieldValidation pending a separate ecommerce validation pass.
This commit is contained in:
@@ -8,10 +8,6 @@ module.exports.createArticle = asyncHandler(async (req, res, next) => {
|
|||||||
try {
|
try {
|
||||||
const loggedUser = await UserService.getLoggedUserById(req.payload.id);
|
const loggedUser = await UserService.getLoggedUserById(req.payload.id);
|
||||||
|
|
||||||
fieldValidation(req.body.article.title, next);
|
|
||||||
fieldValidation(req.body.article.description, next);
|
|
||||||
fieldValidation(req.body.article.body, next);
|
|
||||||
|
|
||||||
const { title, description, body, tagList } = req.body.article;
|
const { title, description, body, tagList } = req.body.article;
|
||||||
const articleSlug = ArticleService.slugify(title);
|
const articleSlug = ArticleService.slugify(title);
|
||||||
const slugInDB = await ArticleService.isSlugUsed(articleSlug);
|
const slugInDB = await ArticleService.isSlugUsed(articleSlug);
|
||||||
@@ -254,8 +250,3 @@ module.exports.deleteFavoriteArticle = asyncHandler(async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const fieldValidation = (field, next) => {
|
|
||||||
if (!field) {
|
|
||||||
return next(new ErrorResponse(`Missing fields`, 400));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -32,10 +32,6 @@ module.exports.authenticate = asyncHandler(async (req, res, next) => {
|
|||||||
|
|
||||||
module.exports.createUser = asyncHandler(async (req, res, next) => {
|
module.exports.createUser = asyncHandler(async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
fieldValidation(req.body.user.username, next);
|
|
||||||
fieldValidation(req.body.user.email, next);
|
|
||||||
fieldValidation(req.body.user.password, next);
|
|
||||||
|
|
||||||
const { username, firstname, lastname, phone, email, password } = req.body.user;
|
const { username, firstname, lastname, phone, email, password } = req.body.user;
|
||||||
const userSlug = UserService.slugify(username);
|
const userSlug = UserService.slugify(username);
|
||||||
const slugInDB = await UserService.isUsernameUsed(userSlug);
|
const slugInDB = await UserService.isUsernameUsed(userSlug);
|
||||||
@@ -92,12 +88,6 @@ module.exports.getUser = asyncHandler(async (req, res, next) => {
|
|||||||
|
|
||||||
module.exports.loginAsUser = asyncHandler(async (req, res, next) => {
|
module.exports.loginAsUser = asyncHandler(async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
if (!req.body.user.email) {
|
|
||||||
return res.status(422).json({ errors: { email: "email can't be blank" } });
|
|
||||||
}
|
|
||||||
if (!req.body.user.password) {
|
|
||||||
return res.status(422).json({ errors: { password: " password can't be blank" } });
|
|
||||||
}
|
|
||||||
passport.authenticate('local', { session: false }, function (err, user, info) {
|
passport.authenticate('local', { session: false }, function (err, user, info) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
@@ -161,7 +151,6 @@ module.exports.updateUserEmail = asyncHandler(async (req, res, next) => {
|
|||||||
return next(new ErrorResponse("Unauthorized", 401, "You are not allowed to access this resource."));
|
return next(new ErrorResponse("Unauthorized", 401, "You are not allowed to access this resource."));
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldValidation(req.body.user.email, next);
|
|
||||||
if (typeof req.body.user.email !== 'undefined') {
|
if (typeof req.body.user.email !== 'undefined') {
|
||||||
user.email = req.body.user.email;
|
user.email = req.body.user.email;
|
||||||
}
|
}
|
||||||
@@ -205,7 +194,6 @@ module.exports.updateUserRole = asyncHandler(async (req, res, next) => {
|
|||||||
return next(new ErrorResponse("Forbidden", 403, "You are not allowed to change user roles."));
|
return next(new ErrorResponse("Forbidden", 403, "You are not allowed to change user roles."));
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldValidation(req.body.user.role, next);
|
|
||||||
if (typeof req.body.user.role !== 'undefined') {
|
if (typeof req.body.user.role !== 'undefined') {
|
||||||
user.role = req.body.user.role;
|
user.role = req.body.user.role;
|
||||||
}
|
}
|
||||||
@@ -226,7 +214,6 @@ module.exports.updateUserUsername = asyncHandler(async (req, res, next) => {
|
|||||||
return next(new ErrorResponse("Unauthorized", 401, "You are not allowed to access this resource."));
|
return next(new ErrorResponse("Unauthorized", 401, "You are not allowed to access this resource."));
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldValidation(req.body.user.username, next);
|
|
||||||
if (typeof req.body.user.username !== 'undefined') {
|
if (typeof req.body.user.username !== 'undefined') {
|
||||||
user.username = req.body.user.username;
|
user.username = req.body.user.username;
|
||||||
}
|
}
|
||||||
@@ -240,8 +227,3 @@ module.exports.updateUserUsername = asyncHandler(async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const fieldValidation = (field, next) => {
|
|
||||||
if (!field) {
|
|
||||||
return next(new ErrorResponse(`Missing fields`, 400));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
const { validationResult } = require('express-validator');
|
||||||
|
|
||||||
|
const validate = (req, res, next) => {
|
||||||
|
const errors = validationResult(req);
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
return res.status(422).json({ errors: errors.mapped() });
|
||||||
|
}
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = validate;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
const { body } = require('express-validator');
|
||||||
|
|
||||||
|
const createArticleRules = [
|
||||||
|
body('article.title')
|
||||||
|
.notEmpty().withMessage("can't be blank")
|
||||||
|
.isLength({ max: 255 }).withMessage('must be at most 255 characters')
|
||||||
|
.trim(),
|
||||||
|
body('article.description')
|
||||||
|
.notEmpty().withMessage("can't be blank")
|
||||||
|
.isLength({ max: 1000 }).withMessage('must be at most 1000 characters')
|
||||||
|
.trim(),
|
||||||
|
body('article.body')
|
||||||
|
.notEmpty().withMessage("can't be blank"),
|
||||||
|
];
|
||||||
|
|
||||||
|
const updateArticleRules = [
|
||||||
|
body('article.title')
|
||||||
|
.optional()
|
||||||
|
.isLength({ max: 255 }).withMessage('must be at most 255 characters')
|
||||||
|
.trim(),
|
||||||
|
body('article.description')
|
||||||
|
.optional()
|
||||||
|
.isLength({ max: 1000 }).withMessage('must be at most 1000 characters')
|
||||||
|
.trim(),
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = { createArticleRules, updateArticleRules };
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
const { body } = require('express-validator');
|
||||||
|
|
||||||
|
const createUserRules = [
|
||||||
|
body('user.email')
|
||||||
|
.notEmpty().withMessage("can't be blank")
|
||||||
|
.isEmail().withMessage('must be a valid email address')
|
||||||
|
.normalizeEmail(),
|
||||||
|
body('user.username')
|
||||||
|
.notEmpty().withMessage("can't be blank")
|
||||||
|
.isLength({ min: 3, max: 50 }).withMessage('must be between 3 and 50 characters')
|
||||||
|
.matches(/^[a-zA-Z0-9_.-]+$/).withMessage('must contain only letters, numbers, underscores, dots or hyphens'),
|
||||||
|
body('user.password')
|
||||||
|
.notEmpty().withMessage("can't be blank")
|
||||||
|
.isLength({ min: 8 }).withMessage('must be at least 8 characters'),
|
||||||
|
];
|
||||||
|
|
||||||
|
const loginRules = [
|
||||||
|
body('user.email')
|
||||||
|
.notEmpty().withMessage("can't be blank")
|
||||||
|
.isEmail().withMessage('must be a valid email address')
|
||||||
|
.normalizeEmail(),
|
||||||
|
body('user.password')
|
||||||
|
.notEmpty().withMessage("can't be blank"),
|
||||||
|
];
|
||||||
|
|
||||||
|
const updateUserRules = [
|
||||||
|
body('user.firstname')
|
||||||
|
.optional()
|
||||||
|
.isLength({ max: 100 }).withMessage('must be at most 100 characters')
|
||||||
|
.trim(),
|
||||||
|
body('user.lastname')
|
||||||
|
.optional()
|
||||||
|
.isLength({ max: 100 }).withMessage('must be at most 100 characters')
|
||||||
|
.trim(),
|
||||||
|
body('user.phone')
|
||||||
|
.optional()
|
||||||
|
.isMobilePhone().withMessage('must be a valid phone number'),
|
||||||
|
body('user.image')
|
||||||
|
.optional()
|
||||||
|
.isURL().withMessage('must be a valid URL'),
|
||||||
|
];
|
||||||
|
|
||||||
|
const updateEmailRules = [
|
||||||
|
body('user.email')
|
||||||
|
.notEmpty().withMessage("can't be blank")
|
||||||
|
.isEmail().withMessage('must be a valid email address')
|
||||||
|
.normalizeEmail(),
|
||||||
|
];
|
||||||
|
|
||||||
|
const updateUsernameRules = [
|
||||||
|
body('user.username')
|
||||||
|
.notEmpty().withMessage("can't be blank")
|
||||||
|
.isLength({ min: 3, max: 50 }).withMessage('must be between 3 and 50 characters')
|
||||||
|
.matches(/^[a-zA-Z0-9_.-]+$/).withMessage('must contain only letters, numbers, underscores, dots or hyphens'),
|
||||||
|
];
|
||||||
|
|
||||||
|
const updateRoleRules = [
|
||||||
|
body('user.role')
|
||||||
|
.notEmpty().withMessage("can't be blank")
|
||||||
|
.isIn(['Admin', 'User']).withMessage('must be Admin or User'),
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = { createUserRules, loginRules, updateUserRules, updateEmailRules, updateUsernameRules, updateRoleRules };
|
||||||
@@ -10,14 +10,16 @@ const {
|
|||||||
deleteFavoriteArticle
|
deleteFavoriteArticle
|
||||||
} = require('../../../controllers/articles.controller');
|
} = require('../../../controllers/articles.controller');
|
||||||
const auth = require('../../../middlewares/auth');
|
const auth = require('../../../middlewares/auth');
|
||||||
|
const validate = require('../../../middlewares/validate');
|
||||||
|
const { createArticleRules, updateArticleRules } = require('../../../middlewares/validators/articles.validators');
|
||||||
|
|
||||||
const articleRouter = express.Router();
|
const articleRouter = express.Router();
|
||||||
|
|
||||||
articleRouter.get('/', auth.optional, getArticles);
|
articleRouter.get('/', auth.optional, getArticles);
|
||||||
articleRouter.post('/', auth.required, createArticle);
|
articleRouter.post('/', auth.required, createArticleRules, validate, createArticle);
|
||||||
articleRouter.get('/feed', auth.required, articlesFeed);
|
articleRouter.get('/feed', auth.required, articlesFeed);
|
||||||
articleRouter.get('/:slug', auth.optional, getArticle);
|
articleRouter.get('/:slug', auth.optional, getArticle);
|
||||||
articleRouter.put('/:slug', auth.required, updateArticle);
|
articleRouter.put('/:slug', auth.required, updateArticleRules, validate, updateArticle);
|
||||||
articleRouter.delete('/:slug', auth.required, deleteArticle);
|
articleRouter.delete('/:slug', auth.required, deleteArticle);
|
||||||
articleRouter.post('/:slug/favorite', auth.required, addFavoriteArticle);
|
articleRouter.post('/:slug/favorite', auth.required, addFavoriteArticle);
|
||||||
articleRouter.delete('/:slug/favorite', auth.required, deleteFavoriteArticle);
|
articleRouter.delete('/:slug/favorite', auth.required, deleteFavoriteArticle);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ const {
|
|||||||
updateUser, updateUserEmail, updateUserPassword, updateUserRole, updateUserUsername
|
updateUser, updateUserEmail, updateUserPassword, updateUserRole, updateUserUsername
|
||||||
} = require('../../../controllers/users.controller');
|
} = require('../../../controllers/users.controller');
|
||||||
const auth = require('../../../middlewares/auth');
|
const auth = require('../../../middlewares/auth');
|
||||||
|
const validate = require('../../../middlewares/validate');
|
||||||
|
const { createUserRules, loginRules, updateUserRules, updateEmailRules, updateUsernameRules, updateRoleRules } = require('../../../middlewares/validators/users.validators');
|
||||||
|
|
||||||
const userRouter = express.Router();
|
const userRouter = express.Router();
|
||||||
|
|
||||||
@@ -17,15 +19,15 @@ const authLimiter = rateLimit({
|
|||||||
});
|
});
|
||||||
|
|
||||||
userRouter.get('/', auth.required, getUser);
|
userRouter.get('/', auth.required, getUser);
|
||||||
userRouter.put('/', auth.required, updateUser);
|
userRouter.put('/', auth.required, updateUserRules, validate, updateUser);
|
||||||
userRouter.post('/', authLimiter, auth.optional, createUser);
|
userRouter.post('/', authLimiter, auth.optional, createUserRules, validate, createUser);
|
||||||
userRouter.get('/authenticate', auth.optional, authenticate);
|
userRouter.get('/authenticate', auth.optional, authenticate);
|
||||||
userRouter.post('/login', authLimiter, auth.optional, loginAsUser);
|
userRouter.post('/login', authLimiter, auth.optional, loginRules, validate, loginAsUser);
|
||||||
//userRouter.post('/register', auth.optional, createUser);
|
//userRouter.post('/register', auth.optional, createUser);
|
||||||
//userRouter.get('/users', auth.required, getAllUsers);
|
//userRouter.get('/users', auth.required, getAllUsers);
|
||||||
userRouter.put('/update/email', auth.required, updateUserEmail);
|
userRouter.put('/update/email', auth.required, updateEmailRules, validate, updateUserEmail);
|
||||||
userRouter.put('/update/password', auth.required, updateUserPassword);
|
userRouter.put('/update/password', auth.required, updateUserPassword);
|
||||||
userRouter.put('/update/role', auth.required, updateUserRole);
|
userRouter.put('/update/role', auth.required, updateRoleRules, validate, updateUserRole);
|
||||||
userRouter.put('/update/username', auth.required, updateUserUsername);
|
userRouter.put('/update/username', auth.required, updateUsernameRules, validate, updateUserUsername);
|
||||||
|
|
||||||
module.exports = userRouter;
|
module.exports = userRouter;
|
||||||
|
|||||||
Reference in New Issue
Block a user