mirror of
https://github.com/swissmakers/fail2ban-ui.git
synced 2026-04-11 13:47:05 +02:00
269 lines
7.7 KiB
JavaScript
269 lines
7.7 KiB
JavaScript
// Validation functions for Fail2ban UI
|
|
|
|
function validateTimeFormat(value, fieldName) {
|
|
if (!value || !value.trim()) return { valid: true }; // Empty is OK
|
|
// Support: s (seconds), m (minutes), h (hours), d (days), w (weeks), mo (months), y (years)
|
|
const timePattern = /^\d+([smhdwy]|mo)$/i;
|
|
if (!timePattern.test(value.trim())) {
|
|
return {
|
|
valid: false,
|
|
message: 'Invalid time format. Use format: 1m = 1 minute, 1h = 1 hour, 1d = 1 day, 1w = 1 week, 1mo = 1 month, 1y = 1 year'
|
|
};
|
|
}
|
|
return { valid: true };
|
|
}
|
|
|
|
function validateMaxRetry(value) {
|
|
if (!value || value.trim() === '') return { valid: true }; // Empty is OK
|
|
const num = parseInt(value, 10);
|
|
if (isNaN(num) || num < 1) {
|
|
return {
|
|
valid: false,
|
|
message: 'Max retry must be a positive integer (minimum 1)'
|
|
};
|
|
}
|
|
return { valid: true };
|
|
}
|
|
|
|
function validateEmail(value) {
|
|
if (!value || !value.trim()) return { valid: true }; // Empty is OK
|
|
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (!emailPattern.test(value.trim())) {
|
|
return {
|
|
valid: false,
|
|
message: 'Invalid email format'
|
|
};
|
|
}
|
|
return { valid: true };
|
|
}
|
|
|
|
// Validate IP address (IPv4, IPv6, CIDR, or hostname)
|
|
function isValidIP(ip) {
|
|
if (!ip || !ip.trim()) return false;
|
|
ip = ip.trim();
|
|
|
|
// Allow hostnames (fail2ban supports DNS hostnames)
|
|
// Basic hostname validation: alphanumeric, dots, hyphens
|
|
const hostnamePattern = /^[a-zA-Z0-9]([a-zA-Z0-9\-\.]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-\.]*[a-zA-Z0-9])?)*$/;
|
|
|
|
// IPv4 with optional CIDR
|
|
const ipv4Pattern = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
|
|
|
|
// IPv6 with optional CIDR (simplified - allows various IPv6 formats)
|
|
const ipv6Pattern = /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}(\/\d{1,3})?$/;
|
|
const ipv6CompressedPattern = /^::([0-9a-fA-F]{0,4}:){0,6}[0-9a-fA-F]{0,4}(\/\d{1,3})?$/;
|
|
const ipv6FullPattern = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}(\/\d{1,3})?$/;
|
|
|
|
// Check IPv4
|
|
if (ipv4Pattern.test(ip)) {
|
|
const parts = ip.split('/');
|
|
const octets = parts[0].split('.');
|
|
for (let octet of octets) {
|
|
const num = parseInt(octet, 10);
|
|
if (num < 0 || num > 255) return false;
|
|
}
|
|
if (parts.length > 1) {
|
|
const cidr = parseInt(parts[1], 10);
|
|
if (cidr < 0 || cidr > 32) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Check IPv6
|
|
if (ipv6Pattern.test(ip) || ipv6CompressedPattern.test(ip) || ipv6FullPattern.test(ip)) {
|
|
if (ip.includes('/')) {
|
|
const parts = ip.split('/');
|
|
const cidr = parseInt(parts[1], 10);
|
|
if (cidr < 0 || cidr > 128) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Check hostname
|
|
if (hostnamePattern.test(ip)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function validateIgnoreIPs() {
|
|
if (typeof getIgnoreIPsArray !== 'function') {
|
|
console.error('getIgnoreIPsArray function not found');
|
|
return { valid: true }; // Skip validation if function not available
|
|
}
|
|
|
|
const ignoreIPs = getIgnoreIPsArray();
|
|
const invalidIPs = [];
|
|
|
|
for (let i = 0; i < ignoreIPs.length; i++) {
|
|
const ip = ignoreIPs[i];
|
|
if (!isValidIP(ip)) {
|
|
invalidIPs.push(ip);
|
|
}
|
|
}
|
|
|
|
if (invalidIPs.length > 0) {
|
|
return {
|
|
valid: false,
|
|
message: 'Invalid IP addresses, CIDR notation, or hostnames: ' + invalidIPs.join(', ')
|
|
};
|
|
}
|
|
|
|
return { valid: true };
|
|
}
|
|
|
|
function showFieldError(fieldId, message) {
|
|
const errorElement = document.getElementById(fieldId + 'Error');
|
|
const inputElement = document.getElementById(fieldId);
|
|
if (errorElement) {
|
|
errorElement.textContent = message;
|
|
errorElement.classList.remove('hidden');
|
|
}
|
|
if (inputElement) {
|
|
inputElement.classList.add('border-red-500');
|
|
inputElement.classList.remove('border-gray-300');
|
|
}
|
|
}
|
|
|
|
function clearFieldError(fieldId) {
|
|
const errorElement = document.getElementById(fieldId + 'Error');
|
|
const inputElement = document.getElementById(fieldId);
|
|
if (errorElement) {
|
|
errorElement.classList.add('hidden');
|
|
errorElement.textContent = '';
|
|
}
|
|
if (inputElement) {
|
|
inputElement.classList.remove('border-red-500');
|
|
inputElement.classList.add('border-gray-300');
|
|
}
|
|
}
|
|
|
|
function validateAllSettings() {
|
|
let isValid = true;
|
|
|
|
// Validate bantime
|
|
const banTime = document.getElementById('banTime');
|
|
if (banTime) {
|
|
const banTimeValidation = validateTimeFormat(banTime.value, 'bantime');
|
|
if (!banTimeValidation.valid) {
|
|
showFieldError('banTime', banTimeValidation.message);
|
|
isValid = false;
|
|
} else {
|
|
clearFieldError('banTime');
|
|
}
|
|
}
|
|
|
|
// Validate findtime
|
|
const findTime = document.getElementById('findTime');
|
|
if (findTime) {
|
|
const findTimeValidation = validateTimeFormat(findTime.value, 'findtime');
|
|
if (!findTimeValidation.valid) {
|
|
showFieldError('findTime', findTimeValidation.message);
|
|
isValid = false;
|
|
} else {
|
|
clearFieldError('findTime');
|
|
}
|
|
}
|
|
|
|
// Validate max retry
|
|
const maxRetry = document.getElementById('maxRetry');
|
|
if (maxRetry) {
|
|
const maxRetryValidation = validateMaxRetry(maxRetry.value);
|
|
if (!maxRetryValidation.valid) {
|
|
showFieldError('maxRetry', maxRetryValidation.message);
|
|
isValid = false;
|
|
} else {
|
|
clearFieldError('maxRetry');
|
|
}
|
|
}
|
|
|
|
// Validate email
|
|
const destEmail = document.getElementById('destEmail');
|
|
if (destEmail) {
|
|
const emailValidation = validateEmail(destEmail.value);
|
|
if (!emailValidation.valid) {
|
|
showFieldError('destEmail', emailValidation.message);
|
|
isValid = false;
|
|
} else {
|
|
clearFieldError('destEmail');
|
|
}
|
|
}
|
|
|
|
// Validate IgnoreIPs
|
|
const ignoreIPsValidation = validateIgnoreIPs();
|
|
if (!ignoreIPsValidation.valid) {
|
|
// Show error for ignoreIPs field
|
|
const errorContainer = document.getElementById('ignoreIPsError');
|
|
if (errorContainer) {
|
|
errorContainer.textContent = ignoreIPsValidation.message;
|
|
errorContainer.classList.remove('hidden');
|
|
}
|
|
if (typeof showToast === 'function') {
|
|
showToast(ignoreIPsValidation.message, 'error');
|
|
}
|
|
isValid = false;
|
|
} else {
|
|
const errorContainer = document.getElementById('ignoreIPsError');
|
|
if (errorContainer) {
|
|
errorContainer.classList.add('hidden');
|
|
errorContainer.textContent = '';
|
|
}
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
// Setup validation on blur for all fields
|
|
function setupFormValidation() {
|
|
const banTimeInput = document.getElementById('banTime');
|
|
const findTimeInput = document.getElementById('findTime');
|
|
const maxRetryInput = document.getElementById('maxRetry');
|
|
const destEmailInput = document.getElementById('destEmail');
|
|
|
|
if (banTimeInput) {
|
|
banTimeInput.addEventListener('blur', function() {
|
|
const validation = validateTimeFormat(this.value, 'bantime');
|
|
if (!validation.valid) {
|
|
showFieldError('banTime', validation.message);
|
|
} else {
|
|
clearFieldError('banTime');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (findTimeInput) {
|
|
findTimeInput.addEventListener('blur', function() {
|
|
const validation = validateTimeFormat(this.value, 'findtime');
|
|
if (!validation.valid) {
|
|
showFieldError('findTime', validation.message);
|
|
} else {
|
|
clearFieldError('findTime');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (maxRetryInput) {
|
|
maxRetryInput.addEventListener('blur', function() {
|
|
const validation = validateMaxRetry(this.value);
|
|
if (!validation.valid) {
|
|
showFieldError('maxRetry', validation.message);
|
|
} else {
|
|
clearFieldError('maxRetry');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (destEmailInput) {
|
|
destEmailInput.addEventListener('blur', function() {
|
|
const validation = validateEmail(this.value);
|
|
if (!validation.valid) {
|
|
showFieldError('destEmail', validation.message);
|
|
} else {
|
|
clearFieldError('destEmail');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|