-
-
Notifications
You must be signed in to change notification settings - Fork 143
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow decoration of functions beyond the admin login (#86)
* Allow decoration of functions beyond the admin login * Exclude tests file from coverage * Allow installing django 1.11 * Add python 3.6 for testing
- Loading branch information
1 parent
d2b712e
commit b985d17
Showing
6 changed files
with
156 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
[run] | ||
omit = *_settings.py, defender/*migrations/*, defender/exampleapp/* | ||
omit = *_settings.py, defender/*migrations/*, defender/exampleapp/*, *test.py, | ||
*__init__.py, *tests.py, *urls.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,54 @@ | ||
from . import utils | ||
|
||
import functools | ||
|
||
def watch_login(func): | ||
|
||
def watch_login(status_code=302, msg=''): | ||
""" | ||
Used to decorate the django.contrib.admin.site.login method. | ||
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. | ||
""" | ||
|
||
def decorated_login(request, *args, **kwargs): | ||
# 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 utils.is_already_locked(request): | ||
return utils.lockout_response(request) | ||
|
||
# call the login function | ||
response = func(request, *args, **kwargs) | ||
|
||
if request.method == 'POST': | ||
# see if the login was successful | ||
login_unsuccessful = ( | ||
response and | ||
not response.has_header('location') and | ||
response.status_code != 302 | ||
) | ||
|
||
# ideally make this background task, but to keep simple, keeping | ||
# it inline for now. | ||
utils.add_login_attempt_to_db(request, not login_unsuccessful) | ||
|
||
if utils.check_request(request, login_unsuccessful): | ||
return response | ||
|
||
return utils.lockout_response(request) | ||
|
||
return response | ||
|
||
def decorated_login(func): | ||
@functools.wraps(func) | ||
def wrapper(request, *args, **kwargs): | ||
# 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 utils.is_already_locked(request): | ||
return utils.lockout_response(request) | ||
|
||
# call the login function | ||
response = func(request, *args, **kwargs) | ||
|
||
if request.method == 'POST': | ||
# see if the login was successful | ||
if status_code == 302: # standard Django login view | ||
login_unsuccessful = ( | ||
response and | ||
not response.has_header('location') and | ||
response.status_code != status_code | ||
) | ||
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 = ( | ||
response and response.status_code == status_code | ||
and msg in response.content.decode('utf-8') | ||
) | ||
|
||
# ideally make this background task, but to keep simple, keeping | ||
# it inline for now. | ||
utils.add_login_attempt_to_db(request, not login_unsuccessful) | ||
|
||
if utils.check_request(request, login_unsuccessful): | ||
return response | ||
|
||
return utils.lockout_response(request) | ||
|
||
return response | ||
|
||
return wrapper | ||
return decorated_login |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters