[ADD] BASE : Add fail2ban feature to Horilla

This commit is contained in:
Horilla
2025-05-07 14:50:45 +05:30
parent b89ac75370
commit b5df3b6e5e

View File

@@ -1,9 +1,16 @@
import logging
import os
import time
from datetime import datetime
from django.apps import apps
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.signals import user_login_failed
from django.db.models import Max, Q
from django.db.models.signals import m2m_changed, post_migrate, post_save
from django.dispatch import receiver
from django.shortcuts import redirect
from base.models import Announcement, PenaltyAccounts
from horilla.methods import get_horilla_model_class
@@ -101,3 +108,107 @@ def filtered_employees(sender, instance, action, **kwargs):
)
instance.filtered_employees.set(employees)
# Logger setup
logger = logging.getLogger("django.security")
# Create a global dictionary to track login attempts and ban time per session
failed_attempts = {}
ban_time = {}
FAIL2BAN_LOG_ENABLED = os.path.exists(
"security.log"
) # Checking that any file is created for the details of the wrong logins.
# The file will be created only if you set the LOGGING in your settings.py
@receiver(user_login_failed)
def log_login_failed(sender, credentials, request, **kwargs):
"""
To ban the IP of user that enter wrong credentials for multiple times
you should add this section in your settings.py file. And also it creates the security file for deatils of wrong logins.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'security_file': {
'level': 'WARNING',
'class': 'logging.FileHandler',
'filename': '/var/log/django/security.log', # File Path for view the log details.
# Give the same path to the section FAIL2BAN_LOG_ENABLED = os.path.exists('security.log') in signals.py in Base.
},
},
'loggers': {
'django.security': {
'handlers': ['security_file'],
'level': 'WARNING',
'propagate': False,
},
},
}
"""
# Checking that the file is created or not to initiate the ban functions.
if not FAIL2BAN_LOG_ENABLED:
return
username = credentials.get("username", "unknown")
ip = request.META.get("REMOTE_ADDR", "unknown")
# Track the number of failed attempts for the user in the current session
session_key = request.session.session_key
if not session_key:
request.session.create()
# Initialize failed attempts for this session if not already
if session_key not in failed_attempts:
failed_attempts[session_key] = 0
# Initialize ban time if not already set
if session_key not in ban_time:
ban_time[session_key] = 0
failed_attempts[session_key] += 1
# Log the failed attempt
logger.warning(f"Invalid login attempt for user '{username}' from {ip}")
# Set maximum allowed attempts
max_attempts = 3
attempts_left = max_attempts - failed_attempts[session_key]
# If the user is banned, show banned message and GIF
if ban_time.get(session_key, 0) > time.time():
banned_until = time.strftime("%H:%M", time.localtime(ban_time[session_key]))
messages.info(
request, f"You are banned until {banned_until}. Please try again later."
)
return redirect("/")
# If failed attempts are above the limit, ban the user for 5 minutes
if failed_attempts[session_key] >= max_attempts:
# Ban user for 5 minutes (300 seconds)
ban_time[session_key] = time.time() + 300
messages.info(
request,
"You have exceeded the maximum attempts. You are banned for 5 minutes.",
)
return redirect("/") # Redirect or show a banned page
# Display message showing remaining attempts
if attempts_left > 0:
message = (
f"You have {attempts_left} attempts left before being temporarily banned."
)
else:
message = (
"You have exceeded the maximum attempts. You will be banned for 5 minutes."
)
# Add a message to the session to show to the user
messages.info(request, message)
return redirect("login")