Skip to content

Commit

Permalink
add optional logging of failed login attempts
Browse files Browse the repository at this point in the history
If LOG_FAILED_LOGINS is True in .env then this will use the
user_login_failed signal to send some basic details about failed logins
if logging is configured. Hopefully helpful to try and work out why
users are unable to log in. Note that this does not include the reason
shown to the user but does say if the account is present, active and has
a marker object.
  • Loading branch information
struan committed Nov 18, 2024
1 parent ab63efe commit c611ff4
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 0 deletions.
2 changes: 2 additions & 0 deletions ceuk-marking/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
HIDE_DEBUG_TOOLBAR=(bool, False),
LOG_LEVEL=(str, "WARNING"),
BRAND=(str, "default"),
LOG_FAILED_LOGINS=(bool, False),
)
environ.Env.read_env(BASE_DIR / ".env")

Expand All @@ -45,6 +46,7 @@
MAPIT_API_KEY = env("MAPIT_API_KEY")
LOG_LEVEL = env("LOG_LEVEL")
BRAND = env("BRAND")
LOG_FAILED_LOGINS = env("LOG_FAILED_LOGINS")

BRAND_TEMPLATES = BASE_DIR / "cobrands" / BRAND

Expand Down
25 changes: 25 additions & 0 deletions crowdsourcer/views/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

from django.conf import settings
from django.contrib.auth.mixins import UserPassesTestMixin
from django.contrib.auth.models import User
from django.contrib.auth.signals import user_login_failed
from django.core.exceptions import PermissionDenied
from django.core.mail import mail_admins
from django.db.models import Count, F, FloatField, OuterRef, Subquery
from django.db.models.functions import Cast
from django.dispatch import receiver
from django.http import JsonResponse
from django.views.generic import ListView, TemplateView

Expand Down Expand Up @@ -639,3 +642,25 @@ def get_context_data(self, **kwargs):
context["do_not_mark_only"] = do_not_mark_only

return context


@receiver(user_login_failed)
def log_failed_login(sender, credentials, **kwargs):
print(f"LOG_FAILED_LOGINS is {settings.LOG_FAILED_LOGINS}")
if not settings.LOG_FAILED_LOGINS:
return

username = credentials.get("username", None)
msg = "User exists"
try:
u = User.objects.get(username=username)
if not u.is_active:
msg = "User exists, is not active"
elif not hasattr(u, "marker"):
msg = "User exists, has no marker"
except User.DoesNotExist:
msg = "User does not exist"

logger.warning(
f"Failed login attempt: Username '{username}' failed to authenticate. {msg}"
)

0 comments on commit c611ff4

Please sign in to comment.