8

I would like to log the user IP address in my Django application, specifically for login, logout and failed login events. I'm using Django builtin functions as follows:

from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
from ipware.ip import get_ip
import logging

logger = logging.getLogger(__name__)

def log_logged_in(sender, user, request, **kwargs):
    logger.info("%s User %s successfully logged in" % (get_ip(request), user))

def log_logged_out(sender, user, request, **kwargs):
    logger.info("%s User %s successfully logged out" % (get_ip(request), user))

def log_login_failed(sender, credentials, **kwargs):
    logger.warning("%s Authentication failure for user %s" % ("...IP...", credentials['username']))

user_logged_in.connect(log_logged_in)
user_logged_out.connect(log_logged_out)
user_login_failed.connect(log_login_failed)

The issue is that I haven't found a way to get the IP for the user_login_failed signal since this function does not have the request in the parameters (https://docs.djangoproject.com/en/1.7/ref/contrib/auth/#module-django.contrib.auth.signals). The credentials parameter is a dictionary that only contains the username and password fields.

How could I get the IP address for this signal?

Many thanks in advance for your help.

Sebastien Damaye
  • 385
  • 2
  • 15

3 Answers3

4

Unfortunately user_login_failed singal don't pass request as argument.

Checkout django-axeshttps://github.com/django-pci/django-axes/

It uses a custom view decorator to track failed logins.

https://github.com/django-pci/django-axes/blob/master/axes/decorators.py#L273

mishbah
  • 5,487
  • 5
  • 25
  • 35
3

I just found in newer Django version (I am using 2.1) has updated this and now it includes the request object in the user_login_failed signal:

https://docs.djangoproject.com/en/2.1/ref/contrib/auth/#django.contrib.auth.signals.user_login_failed

Raymond Chen
  • 419
  • 5
  • 13
0

You could override the login form and intercept it there. It has the request at that stage.

import logging
from django.contrib.admin.forms import AdminAuthenticationForm
from django import forms

log = logging.getLogger(__name__)


class AuthenticationForm(AdminAuthenticationForm):
    def clean(self):
        # to cover more complex cases:
        # http://stackoverflow.com/questions/4581789/how-do-i-get-user-ip-address-in-django
        ip = request.META.get('REMOTE_ADDR')
        try:
            data = super(AuthenticationForm, self).clean()
        except forms.ValidationError:
            log.info('Login Failed (%s) from (%s)', self.cleaned_data.get('username'), ip)
            raise

        if bool(self.user_cache):
            log.info('Login Success (%s) from (%s)', self.cleaned_data.get('username'), ip)
        else:
            log.info('Login Failed (%s) from (%s)', self.cleaned_data.get('username'), ip)

        return data

To install it into the site you need to attach it to django.contrib.admin.site.login_form

I would suggest doing it in your App's ready() method like so:

from django.contrib.admin import site as admin_site

class Config(AppConfig):
    ...

    def ready(self):
        # Attach the logging hook to the login form
        from .forms import AuthenticationForm
        admin_site.login_form = AuthenticationForm
mic159
  • 11
  • 1
  • 2