Drop support Django < 2.2 and add support for Django > 3

This commit is contained in:
Hasan Ramezani 2020-11-23 18:43:50 +01:00
parent 5e6e52fcbb
commit cc06ab33fd
12 changed files with 42 additions and 163 deletions

View file

@ -2,7 +2,6 @@ language: python
dist: xenial
cache: pip
python:
- '3.5'
- '3.6'
- '3.7'
- '3.8'

View file

@ -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

View file

@ -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

View file

@ -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"]

View file

@ -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

View file

@ -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.

View file

@ -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",
),

View file

@ -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

View file

@ -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
```

View file

@ -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
View file

@ -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]