Exemple #1
0
def lockout_response(request):
    if gs("LOCKOUT_TEMPLATE"):
        context = {"cooloff_time": gs("COOLOFF_TIME"), "failure_limit": gs("FAILURE_LIMIT")}
        return render_to_response(gs("LOCKOUT_TEMPLATE"), context, context_instance=RequestContext(request))

    LOCKOUT_URL = get_lockout_url()
    if LOCKOUT_URL:
        return HttpResponseRedirect(LOCKOUT_URL)

    if gs("COOLOFF_TIME"):
        return HttpResponse("Account locked: too many login attempts.  " "Please try again later.")
    else:
        return HttpResponse("Account locked: too many login attempts.  " "Contact an admin to unlock your account.")
Exemple #2
0
def get_ip(request):
    if not gs("BEHIND_REVERSE_PROXY"):
        ip = request.META.get("REMOTE_ADDR", "")
    else:
        logging.debug(
            "Axes is configured to be behind reverse proxy...looking for header value %s", gs("REVERSE_PROXY_HEADER")
        )
        ip = request.META.get(gs("REVERSE_PROXY_HEADER"), "")
        if ip == "":
            raise Warning(
                "Axes is configured for operation behind a reverse proxy but could not find "
                "an HTTP header value {0}. Check your proxy server settings "
                "to make sure this header value is being passed.".format(gs("REVERSE_PROXY_HEADER"))
            )
    return ip
Exemple #3
0
def is_already_locked(request):
    ip = get_ip(request)

    if gs("ONLY_WHITELIST"):
        if not ip_in_whitelist(ip):
            return True

    if ip_in_blacklist(ip):
        return True

    attempts = get_user_attempts(request)
    user_lockable = is_user_lockable(request)
    for attempt in attempts:
        if attempt.failures_since_start >= gs("FAILURE_LIMIT") and gs("LOCK_OUT_AT_FAILURE") and user_lockable:
            return True

    return False
Exemple #4
0
def get_user_attempts(request):
    """
    Returns access attempt record if it exists.
    Otherwise return None.
    """
    ip = get_ip(request)

    username = request.POST.get("username", None)

    if gs("USE_USER_AGENT"):
        ua = request.META.get("HTTP_USER_AGENT", "<unknown>")
        attempts = AccessAttempt.objects.filter(user_agent=ua, ip_address=ip, username=username, trusted=True)
    else:
        attempts = AccessAttempt.objects.filter(ip_address=ip, username=username, trusted=True)

    if len(attempts) == 0:
        params = {"ip_address": ip, "trusted": False}
        if gs("USE_USER_AGENT"):
            params["user_agent"] = ua

        attempts = AccessAttempt.objects.filter(**params)
        if username and not ip_in_whitelist(ip):
            del params["ip_address"]
            params["username"] = username
            attempts |= AccessAttempt.objects.filter(**params)

    attempts = list(attempts)
    if gs("COOLOFF_TIME"):
        for attempt in attempts:
            COOLOFF_TIME = gs("COOLOFF_TIME")
            if isinstance(COOLOFF_TIME, int) or isinstance(COOLOFF_TIME, float):
                COOLOFF_TIME = timedelta(hours=COOLOFF_TIME)
            if attempt.attempt_time + COOLOFF_TIME < datetime.now() and attempt.trusted is False:
                attempt.delete()
                attempts.pop(attempts.index(attempt))
    return attempts
Exemple #5
0
    def decorated_login(request, *args, **kwargs):
        # share some useful information
        if func.__name__ != "decorated_login" and gs("VERBOSE"):
            log.debug("AXES: Calling decorated function: %s" % func.__name__)
            if args:
                log.debug("args: %s" % args)
            if kwargs:
                log.debug("kwargs: %s" % kwargs)

        # TODO: create a class to hold the attempts records and perform checks
        # with its methods? or just store attempts=get_user_attempts here and
        # pass it to the functions
        # also no need to keep accessing these:
        # ip = request.META.get('REMOTE_ADDR', '')
        # ua = request.META.get('HTTP_USER_AGENT', '<unknown>')
        # username = request.POST.get('username', None)

        # if the request is currently under lockout, do not proceed to the
        # login function, go directly to lockout url, do not pass go, do not
        # collect messages about this login attempt
        if is_already_locked(request):
            return lockout_response(request)

        # call the login function
        response = func(request, *args, **kwargs)

        if func.__name__ == "decorated_login":
            # if we're dealing with this function itself, don't bother checking
            # for invalid login attempts.  I suppose there's a bunch of
            # recursion going on here that used to cause one failed login
            # attempt to generate 10+ failed access attempt records (with 3
            # failed attempts each supposedly)
            return response

        if request.method == "POST":
            # see if the login was successful
            login_unsuccessful = response and not response.has_header("location") and response.status_code != 302
            log_access_request(request, login_unsuccessful)
            if check_request(request, login_unsuccessful):
                return response

            return lockout_response(request)
        return response
Exemple #6
0
def ip_in_blacklist(ip):
    if gs("IP_BLACKLIST") is not None:
        return ip in gs("IP_BLACKLIST")
    else:
        return False
Exemple #7
0
def ip_in_whitelist(ip):
    if gs("IP_WHITELIST") is not None:
        return ip in gs("IP_WHITELIST")
    else:
        return False
Exemple #8
0
def check_request(request, login_unsuccessful):
    ip_address = get_ip(request)
    username = request.POST.get("username", None)
    failures = 0
    attempts = get_user_attempts(request)

    for attempt in attempts:
        failures = max(failures, attempt.failures_since_start)

    if login_unsuccessful:
        # add a failed attempt for this user
        failures += 1

        # Create an AccessAttempt record if the login wasn't successful
        # has already attempted, update the info
        if len(attempts):
            for attempt in attempts:
                attempt.get_data = "%s\n---------\n%s" % (attempt.get_data, query2str(request.GET.items()))
                attempt.post_data = "%s\n---------\n%s" % (attempt.post_data, query2str(request.POST.items()))
                attempt.http_accept = request.META.get("HTTP_ACCEPT", "<unknown>")
                attempt.path_info = request.META.get("PATH_INFO", "<unknown>")
                attempt.failures_since_start = failures
                attempt.attempt_time = datetime.now()
                attempt.save()
                log.info(
                    "AXES: Repeated login failure by %s. Updating access "
                    "record. Count = %s" % (attempt.ip_address, failures)
                )
        else:
            create_new_failure_records(request, failures)
    else:
        # user logged in -- forget the failed attempts
        failures = 0
        trusted_record_exists = False
        for attempt in attempts:
            if not attempt.trusted:
                attempt.delete()
            else:
                trusted_record_exists = True
                attempt.failures_since_start = 0
                attempt.save()

        if trusted_record_exists is False:
            create_new_trusted_record(request)

    user_lockable = is_user_lockable(request)
    # no matter what, we want to lock them out if they're past the number of
    # attempts allowed, unless the user is set to notlockable
    if failures >= gs("FAILURE_LIMIT") and gs("LOCK_OUT_AT_FAILURE") and user_lockable:
        # We log them out in case they actually managed to enter the correct
        # password
        logout(request)
        log.warn("AXES: locked out %s after repeated login attempts." % (ip_address,))
        # send signal when someone is locked out.
        user_locked_out.send("axes", request=request, username=username, ip_address=ip_address)

        # if a trusted login has violated lockout, revoke trust
        for attempt in [a for a in attempts if a.trusted]:
            attempt.delete()
            create_new_failure_records(request, failures)

        return False

    return True
Exemple #9
0
def ip_in_whitelist(ip):
    if gs("IP_WHITELIST") is not None:
        return ip in gs("IP_WHITELIST")
    else:
        return False


def ip_in_blacklist(ip):
    if gs("IP_BLACKLIST") is not None:
        return ip in gs("IP_BLACKLIST")
    else:
        return False


log = logging.getLogger(gs("LOGGER"))
if gs("VERBOSE"):
    log.debug("AXES: BEGIN LOG")
    log.debug("Using django-axes " + axes.get_version())


def is_user_lockable(request):
    """ Check if the user has a profile with nolockout
    If so, then return the value to see if this user is special
    and doesn't get their account locked out """
    username = request.POST.get("username", None)
    try:
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        # not a valid user
        return True