mirror of
https://github.com/jazzband/django-defender.git
synced 2026-03-16 22:10:32 +00:00
Drop support Django < 2.2 and add support for Django > 3
This commit is contained in:
parent
5e6e52fcbb
commit
cc06ab33fd
12 changed files with 42 additions and 163 deletions
|
|
@ -2,7 +2,6 @@ language: python
|
|||
dist: xenial
|
||||
cache: pip
|
||||
python:
|
||||
- '3.5'
|
||||
- '3.6'
|
||||
- '3.7'
|
||||
- '3.8'
|
||||
|
|
|
|||
16
README.rst
16
README.rst
|
|
@ -10,6 +10,10 @@ django-defender
|
|||
:alt: Supported Python versions
|
||||
:target: https://pypi.org/project/django-defender/
|
||||
|
||||
.. image:: https://img.shields.io/pypi/djversions/django-defender.svg
|
||||
:target: https://pypi.org/project/django-defender/
|
||||
:alt: Supported Django versions
|
||||
|
||||
.. image:: https://travis-ci.org/jazzband/django-defender.svg
|
||||
:target: https://travis-ci.org/jazzband/django-defender
|
||||
:alt: Build Status
|
||||
|
|
@ -104,8 +108,8 @@ Admin pages
|
|||
Requirements
|
||||
------------
|
||||
|
||||
* Python: 3.5, 3.6, 3.7, 3.8, PyPy
|
||||
* Django: 1.11, 2.1, 2.2, 3.x
|
||||
* Python: 3.6, 3.7, 3.8, PyPy
|
||||
* Django: 2.2, 3.x
|
||||
* Redis
|
||||
|
||||
|
||||
|
|
@ -165,8 +169,8 @@ following to your ``urls.py``
|
|||
.. code-block:: python
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^admin/', include(admin.site.urls)), # normal admin
|
||||
url(r'^admin/defender/', include('defender.urls')), # defender admin
|
||||
path('admin/', include(admin.site.urls)), # normal admin
|
||||
path('admin/defender/', include('defender.urls')), # defender admin
|
||||
# your own patterns follow...
|
||||
]
|
||||
|
||||
|
|
@ -674,13 +678,13 @@ like:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
PYTHONPATH=$PYTHONPATH:$PWD django-admin.py test defender --settings=defender.test_settings
|
||||
PYTHONPATH=$PYTHONPATH:$PWD django-admin test defender --settings=defender.test_settings
|
||||
|
||||
With Code coverage:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
PYTHONPATH=$PYTHONPATH:$PWD coverage run --source=defender $(which django-admin.py) test defender --settings=defender.test_settings
|
||||
PYTHONPATH=$PYTHONPATH:$PWD coverage run --source=defender $(which django-admin) test defender --settings=defender.test_settings
|
||||
|
||||
|
||||
Releasing
|
||||
|
|
|
|||
|
|
@ -1,33 +1,27 @@
|
|||
try:
|
||||
from django.utils.deprecation import MiddlewareMixin as MIDDLEWARE_BASE_CLASS
|
||||
except ImportError:
|
||||
MIDDLEWARE_BASE_CLASS = object
|
||||
from django.contrib.auth import views as auth_views
|
||||
from django.contrib.auth.views import LoginView
|
||||
from django.utils.decorators import method_decorator
|
||||
|
||||
from .decorators import watch_login
|
||||
|
||||
|
||||
class FailedLoginMiddleware(MIDDLEWARE_BASE_CLASS):
|
||||
class FailedLoginMiddleware:
|
||||
""" Failed login middleware """
|
||||
|
||||
patched = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
# Watch the auth login.
|
||||
# Monkey-patch only once - otherwise we would be recording
|
||||
# failed attempts multiple times!
|
||||
if not FailedLoginMiddleware.patched:
|
||||
# Django 1.11 turned the `login` function view into the
|
||||
# `LoginView` class-based view
|
||||
try:
|
||||
from django.contrib.auth.views import LoginView
|
||||
|
||||
our_decorator = watch_login()
|
||||
watch_login_method = method_decorator(our_decorator)
|
||||
LoginView.dispatch = watch_login_method(LoginView.dispatch)
|
||||
except ImportError: # Django < 1.11
|
||||
auth_views.login = watch_login()(auth_views.login)
|
||||
our_decorator = watch_login()
|
||||
watch_login_method = method_decorator(our_decorator)
|
||||
LoginView.dispatch = watch_login_method(LoginView.dispatch)
|
||||
|
||||
FailedLoginMiddleware.patched = True
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
return response
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
"""Initial Migration for Defender"""
|
||||
|
||||
def forwards(self, orm):
|
||||
""" Adding model 'AccessAttempt' """
|
||||
db.create_table(
|
||||
"defender_accessattempt",
|
||||
(
|
||||
("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)),
|
||||
(
|
||||
"user_agent",
|
||||
self.gf("django.db.models.fields.CharField")(max_length=255),
|
||||
),
|
||||
(
|
||||
"ip_address",
|
||||
self.gf("django.db.models.fields.GenericIPAddressField")(
|
||||
max_length=39, null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"username",
|
||||
self.gf("django.db.models.fields.CharField")(
|
||||
max_length=255, null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"http_accept",
|
||||
self.gf("django.db.models.fields.CharField")(max_length=1025),
|
||||
),
|
||||
(
|
||||
"path_info",
|
||||
self.gf("django.db.models.fields.CharField")(max_length=255),
|
||||
),
|
||||
(
|
||||
"attempt_time",
|
||||
self.gf("django.db.models.fields.DateTimeField")(
|
||||
auto_now_add=True, blank=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"login_valid",
|
||||
self.gf("django.db.models.fields.BooleanField")(default=False),
|
||||
),
|
||||
),
|
||||
)
|
||||
db.send_create_signal("defender", ["AccessAttempt"])
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'AccessAttempt'
|
||||
db.delete_table("defender_accessattempt")
|
||||
|
||||
models = {
|
||||
"defender.accessattempt": {
|
||||
"Meta": {"ordering": "[u'-attempt_time']", "object_name": "AccessAttempt"},
|
||||
"attempt_time": (
|
||||
"django.db.models.fields.DateTimeField",
|
||||
[],
|
||||
{"auto_now_add": "True", "blank": "True"},
|
||||
),
|
||||
"http_accept": (
|
||||
"django.db.models.fields.CharField",
|
||||
[],
|
||||
{"max_length": "1025"},
|
||||
),
|
||||
"id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}),
|
||||
"ip_address": (
|
||||
"django.db.models.fields.GenericIPAddressField",
|
||||
[],
|
||||
{"max_length": "39", "null": "True"},
|
||||
),
|
||||
"login_valid": (
|
||||
"django.db.models.fields.BooleanField",
|
||||
[],
|
||||
{"default": "False"},
|
||||
),
|
||||
"path_info": (
|
||||
"django.db.models.fields.CharField",
|
||||
[],
|
||||
{"max_length": "255"},
|
||||
),
|
||||
"user_agent": (
|
||||
"django.db.models.fields.CharField",
|
||||
[],
|
||||
{"max_length": "255"},
|
||||
),
|
||||
"username": (
|
||||
"django.db.models.fields.CharField",
|
||||
[],
|
||||
{"max_length": "255", "null": "True"},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ["defender"]
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from django.conf.urls import url, include
|
||||
from django.urls import path
|
||||
from django.contrib import admin
|
||||
|
||||
from .urls import urlpatterns as original_urlpatterns
|
||||
|
||||
urlpatterns = [url(r"^admin/", admin.site.urls),] + original_urlpatterns
|
||||
urlpatterns = [path("admin/", admin.site.urls),] + original_urlpatterns
|
||||
|
|
|
|||
|
|
@ -1,21 +1,15 @@
|
|||
import random
|
||||
import string
|
||||
import time
|
||||
from distutils.version import StrictVersion
|
||||
from unittest.mock import patch
|
||||
|
||||
from django import get_version
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.contrib.sessions.backends.db import SessionStore
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.test.client import RequestFactory
|
||||
from redis.client import Redis
|
||||
|
||||
try:
|
||||
from django.urls import reverse
|
||||
except ImportError:
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.urls import reverse
|
||||
|
||||
from . import utils
|
||||
from . import config
|
||||
|
|
@ -33,8 +27,6 @@ from .test import DefenderTestCase, DefenderTransactionTestCase
|
|||
LOGIN_FORM_KEY = '<form action="/admin/login/" method="post" id="login-form">'
|
||||
ADMIN_LOGIN_URL = reverse("admin:login")
|
||||
|
||||
DJANGO_VERSION = StrictVersion(get_version())
|
||||
|
||||
VALID_USERNAME = VALID_PASSWORD = "valid"
|
||||
UPPER_USERNAME = "VALID"
|
||||
|
||||
|
|
@ -400,12 +392,7 @@ class AccessAttemptTest(DefenderTestCase):
|
|||
# Check if we are in the same login page
|
||||
self.assertContains(response, LOGIN_FORM_KEY)
|
||||
|
||||
# RFC 7231 allows relative URIs in Location header.
|
||||
# Django from version 1.9 is support this:
|
||||
# https://docs.djangoproject.com/en/1.9/releases/1.9/#http-redirects-no-longer-forced-to-absolute-uris
|
||||
lockout_url = "http://testserver/o/login/"
|
||||
if DJANGO_VERSION >= StrictVersion("1.9"):
|
||||
lockout_url = "/o/login/"
|
||||
lockout_url = "/o/login/"
|
||||
|
||||
# So, we shouldn't have gotten a lock-out yet.
|
||||
# But we should get one now, check redirect make sure it is valid.
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
from django.conf.urls import url
|
||||
from django.urls import path, re_path
|
||||
from .views import block_view, unblock_ip_view, unblock_username_view
|
||||
|
||||
urlpatterns = [
|
||||
url(r"^blocks/$", block_view, name="defender_blocks_view"),
|
||||
url(
|
||||
r"^blocks/ip/(?P<ip_address>[A-Za-z0-9-._]+)/unblock$",
|
||||
path("blocks/", block_view, name="defender_blocks_view"),
|
||||
re_path(
|
||||
"blocks/ip/(?P<ip_address>[A-Za-z0-9-._]+)/unblock",
|
||||
unblock_ip_view,
|
||||
name="defender_unblock_ip_view",
|
||||
),
|
||||
url(
|
||||
r"^blocks/username/(?P<username>[\w]+[^\/]*)/unblock$",
|
||||
path(
|
||||
"blocks/username/<path:username>/unblock",
|
||||
unblock_username_view,
|
||||
name="defender_unblock_username_view",
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
from django.shortcuts import render
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.urls import reverse
|
||||
|
||||
try:
|
||||
from django.urls import reverse
|
||||
except ImportError:
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from .utils import get_blocked_ips, get_blocked_usernames, unblock_ip, unblock_username
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ This is just a simple example app, used for testing and showing how things work
|
|||
```
|
||||
mkdir -p exampleapp/static exampleapp/media/static
|
||||
|
||||
PYTHONPATH=$PYTHONPATH:$PWD django-admin.py collectstatic --noinput --settings=exampleapp.settings
|
||||
PYTHONPATH=$PYTHONPATH:$PWD django-admin collectstatic --noinput --settings=exampleapp.settings
|
||||
|
||||
PYTHONPATH=$PYTHONPATH:$PWD django-admin.py runserver --settings=exampleapp.settings
|
||||
PYTHONPATH=$PYTHONPATH:$PWD django-admin runserver --settings=exampleapp.settings
|
||||
```
|
||||
|
|
|
|||
3
setup.py
3
setup.py
|
|
@ -31,6 +31,9 @@ setup(
|
|||
classifiers=[
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Framework :: Django",
|
||||
"Framework :: Django :: 2.2",
|
||||
"Framework :: Django :: 3.0",
|
||||
"Framework :: Django :: 3.1",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Operating System :: OS Independent",
|
||||
|
|
|
|||
13
tox.ini
13
tox.ini
|
|
@ -1,16 +1,11 @@
|
|||
[tox]
|
||||
envlist =
|
||||
# list of supported Django/Python versioons:
|
||||
# https://docs.djangoproject.com/en/2.2/faq/install/#what-python-version-can-i-use-with-django
|
||||
py{35,36,37,py3}-django111
|
||||
py35-django(21,22)
|
||||
py{36,37,py3}-django{21,22,master}
|
||||
py38-django22
|
||||
py{36,37,38,py3}-django{22,30,31,master}
|
||||
py38-{lint,docs}
|
||||
|
||||
[travis]
|
||||
python =
|
||||
3.5: py35
|
||||
3.6: py36
|
||||
3.7: py37
|
||||
3.8: py38
|
||||
|
|
@ -19,13 +14,13 @@ python =
|
|||
[testenv]
|
||||
deps =
|
||||
-rrequirements.txt
|
||||
django111: django>=1.11,<2.0
|
||||
django21: django>=2.1,<2.2
|
||||
django22: django>=2.2,<2.3
|
||||
django30: django>=3.0,<3.1
|
||||
django31: django>=3.1,<3.2
|
||||
djangomaster: https://github.com/django/django/archive/master.tar.gz
|
||||
usedevelop = True
|
||||
commands =
|
||||
{envbindir}/coverage run --source=defender {envbindir}/django-admin.py test defender --settings=defender.travis_settings
|
||||
{envbindir}/coverage run --source=defender {envbindir}/django-admin test defender --settings=defender.travis_settings
|
||||
{envbindir}/coverage report -m
|
||||
|
||||
[testenv:py38-docs]
|
||||
|
|
|
|||
Loading…
Reference in a new issue