104 lines
3.1 KiB
JavaScript
104 lines
3.1 KiB
JavaScript
const Joi = require('joi');
|
|
|
|
const isStrongPassword = (value, helpers) => {
|
|
const rules = [
|
|
{ test: value.length >= 8, message: 'at least 8 characters' },
|
|
{ test: /[A-Z]/.test(value), message: 'one uppercase letter' },
|
|
{ test: /[a-z]/.test(value), message: 'one lowercase letter' },
|
|
{ test: /\d/.test(value), message: 'one number' },
|
|
{ test: /[^A-Za-z0-9]/.test(value), message: 'one special character' },
|
|
];
|
|
|
|
const missing = rules.filter((rule) => !rule.test).map((rule) => rule.message);
|
|
if (missing.length > 0) {
|
|
return helpers.message(`Password must include ${missing.join(', ')}.`);
|
|
}
|
|
|
|
return value;
|
|
};
|
|
|
|
const passwordRuleCheckers = [
|
|
{ test: (value) => value.length >= 8, label: 'al menos 8 caracteres' },
|
|
{ test: (value) => /[A-Z]/.test(value), label: 'una mayúscula' },
|
|
{ test: (value) => /[a-z]/.test(value), label: 'una minúscula' },
|
|
{ test: (value) => /\d/.test(value), label: 'un número' },
|
|
{ test: (value) => /[^A-Za-z0-9]/.test(value), label: 'un carácter especial' },
|
|
];
|
|
|
|
const getPasswordIssues = (value = '') => passwordRuleCheckers
|
|
.filter((rule) => !rule.test(value))
|
|
.map((rule) => rule.label);
|
|
|
|
const loginSchema = Joi.object({
|
|
email: Joi.string().email().required(),
|
|
password: Joi.string().required(),
|
|
});
|
|
|
|
const refreshSchema = Joi.object({
|
|
refreshToken: Joi.string().optional(),
|
|
});
|
|
|
|
const changePasswordSchema = Joi.object({
|
|
currentPassword: Joi.string().required(),
|
|
newPassword: Joi.string().max(100).required().custom(isStrongPassword),
|
|
});
|
|
|
|
const unlockAccountSchema = Joi.object({
|
|
certPassword: Joi.string().required(),
|
|
newPassword: Joi.string().max(100).required().custom(isStrongPassword),
|
|
});
|
|
|
|
const validateRegisterMultipart = (req, res, next) => {
|
|
const { email, password, certPassword, consentAccepted } = req.body;
|
|
const issues = [];
|
|
|
|
if (!email || Joi.string().email().validate(email).error) {
|
|
issues.push('Correo electrónico inválido');
|
|
}
|
|
|
|
if (!password) {
|
|
issues.push('La contraseña es obligatoria');
|
|
} else {
|
|
const passwordIssues = getPasswordIssues(password);
|
|
if (passwordIssues.length > 0) {
|
|
issues.push(`La contraseña debe incluir ${passwordIssues.join(', ')}`);
|
|
}
|
|
}
|
|
|
|
if (!certPassword) {
|
|
issues.push('La contraseña del certificado es obligatoria para firmar los documentos legales');
|
|
}
|
|
|
|
if (!req.file) {
|
|
issues.push('Debes cargar un certificado P12 o PFX');
|
|
}
|
|
|
|
if (!['true', true, 'on', '1', 1].includes(consentAccepted)) {
|
|
issues.push('Debes aceptar los documentos legales para completar el registro');
|
|
}
|
|
|
|
if (issues.length > 0) {
|
|
return res.status(400).json({ success: false, data: null, error: issues.join('; ') });
|
|
}
|
|
|
|
return next();
|
|
};
|
|
|
|
const validate = (schema) => (req, res, next) => {
|
|
const { error } = schema.validate(req.body, { abortEarly: false });
|
|
if (error) {
|
|
const messages = error.details.map((d) => d.message);
|
|
return res.status(400).json({ success: false, data: null, error: messages.join('; ') });
|
|
}
|
|
next();
|
|
};
|
|
|
|
module.exports = {
|
|
validate,
|
|
validateRegisterMultipart,
|
|
loginSchema,
|
|
refreshSchema,
|
|
changePasswordSchema,
|
|
unlockAccountSchema,
|
|
};
|