2014-12-31 22:00:45 +00:00
|
|
|
from . import utils
|
2014-09-24 00:31:17 +00:00
|
|
|
|
2017-06-26 16:23:23 +00:00
|
|
|
import functools
|
2014-09-24 00:31:17 +00:00
|
|
|
|
2017-06-26 16:23:23 +00:00
|
|
|
|
2019-11-15 18:22:14 +00:00
|
|
|
def watch_login(status_code=302, msg="", get_username=utils.get_username_from_request):
|
2014-09-24 00:31:17 +00:00
|
|
|
"""
|
2017-06-26 16:23:23 +00:00
|
|
|
Used to decorate the django.contrib.admin.site.login method or
|
|
|
|
|
any other function you want to protect by brute forcing.
|
|
|
|
|
To make it work on normal functions just pass the status code that should
|
|
|
|
|
indicate a failure and/or a string that will be checked within the
|
|
|
|
|
response body.
|
2014-09-24 00:31:17 +00:00
|
|
|
"""
|
2019-11-15 18:22:14 +00:00
|
|
|
|
2017-06-26 16:23:23 +00:00
|
|
|
def decorated_login(func):
|
|
|
|
|
@functools.wraps(func)
|
|
|
|
|
def wrapper(request, *args, **kwargs):
|
|
|
|
|
# if the request is currently under lockout, do not proceed to the
|
2018-05-29 14:32:08 +00:00
|
|
|
# login function, go directly to lockout url, do not pass go,
|
|
|
|
|
# do not collect messages about this login attempt
|
2023-11-09 12:41:49 +00:00
|
|
|
username = get_username(request)
|
|
|
|
|
|
|
|
|
|
if utils.is_already_locked(request, username=username):
|
|
|
|
|
return utils.lockout_response(request, username=username)
|
2017-06-26 16:23:23 +00:00
|
|
|
|
|
|
|
|
# call the login function
|
|
|
|
|
response = func(request, *args, **kwargs)
|
|
|
|
|
|
2019-11-15 18:22:14 +00:00
|
|
|
if request.method == "POST":
|
2017-06-26 16:23:23 +00:00
|
|
|
# see if the login was successful
|
|
|
|
|
if status_code == 302: # standard Django login view
|
|
|
|
|
login_unsuccessful = (
|
2019-11-15 18:22:14 +00:00
|
|
|
response
|
|
|
|
|
and not response.has_header("location")
|
|
|
|
|
and response.status_code != status_code
|
2017-06-26 16:23:23 +00:00
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
# If msg is not passed the last condition will be evaluated
|
|
|
|
|
# always to True so the first 2 will decide the result.
|
|
|
|
|
login_unsuccessful = (
|
2019-11-15 18:22:14 +00:00
|
|
|
response
|
|
|
|
|
and response.status_code == status_code
|
|
|
|
|
and msg in response.content.decode("utf-8")
|
2017-06-26 16:23:23 +00:00
|
|
|
)
|
|
|
|
|
|
2018-05-29 14:32:08 +00:00
|
|
|
# ideally make this background task, but to keep simple,
|
|
|
|
|
# keeping it inline for now.
|
2019-11-15 18:22:14 +00:00
|
|
|
utils.add_login_attempt_to_db(
|
2023-11-09 12:41:49 +00:00
|
|
|
request, not login_unsuccessful, username=username
|
2019-11-15 18:22:14 +00:00
|
|
|
)
|
2017-06-26 16:23:23 +00:00
|
|
|
|
2023-11-09 12:41:49 +00:00
|
|
|
if utils.check_request(request, login_unsuccessful, username=username):
|
2017-06-26 16:23:23 +00:00
|
|
|
return response
|
|
|
|
|
|
2023-11-09 12:41:49 +00:00
|
|
|
return utils.lockout_response(request, username=username)
|
2017-06-26 16:23:23 +00:00
|
|
|
|
|
|
|
|
return response
|
|
|
|
|
|
|
|
|
|
return wrapper
|
2019-11-15 18:22:14 +00:00
|
|
|
|
2014-09-24 00:31:17 +00:00
|
|
|
return decorated_login
|