Merge pull request #5649 from jennifer-richards/django40
chore: Upgrade to Django 4.0
This commit is contained in:
commit
cb259e9a2a
|
@ -0,0 +1,36 @@
|
|||
# Generated by Django 4.0.10 on 2023-05-16 20:36
|
||||
|
||||
from django.db import migrations
|
||||
import django.db.models.deletion
|
||||
import ietf.utils.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('person', '0001_initial'),
|
||||
('doc', '0003_remove_document_info_order'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='dochistory',
|
||||
name='ad',
|
||||
field=ietf.utils.models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ad_%(class)s_set', to='person.person', verbose_name='area director'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dochistory',
|
||||
name='shepherd',
|
||||
field=ietf.utils.models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='shepherd_%(class)s_set', to='person.email'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='document',
|
||||
name='ad',
|
||||
field=ietf.utils.models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ad_%(class)s_set', to='person.person', verbose_name='area director'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='document',
|
||||
name='shepherd',
|
||||
field=ietf.utils.models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='shepherd_%(class)s_set', to='person.email'),
|
||||
),
|
||||
]
|
|
@ -13,6 +13,7 @@ import warnings
|
|||
from typing import Any, Dict, List, Tuple # pyflakes:ignore
|
||||
|
||||
warnings.simplefilter("always", DeprecationWarning)
|
||||
warnings.filterwarnings("ignore", message="'oidc_provider' defines default_app_config") # hopefully only need until Django 4.1 or 4.2
|
||||
warnings.filterwarnings("ignore", message="'urllib3\\[secure\\]' extra is deprecated")
|
||||
warnings.filterwarnings("ignore", message="The logout\\(\\) view is superseded by")
|
||||
warnings.filterwarnings("ignore", message="Report.file_reporters will no longer be available in Coverage.py 4.2", module="coverage.report")
|
||||
|
@ -100,7 +101,13 @@ SITE_ID = 1
|
|||
# to load the internationalization machinery.
|
||||
USE_I18N = False
|
||||
|
||||
# Django 4.0 changed the default setting of USE_L10N to True. The setting
|
||||
# is deprecated and will be removed in Django 5.0.
|
||||
USE_L10N = False
|
||||
|
||||
USE_TZ = True
|
||||
USE_DEPRECATED_PYTZ = True # supported until Django 5
|
||||
|
||||
|
||||
# Default primary key field type to use for models that don’t have a field with primary_key=True.
|
||||
# In the future (relative to 4.2), the default will become 'django.db.models.BigAutoField.'
|
||||
|
@ -319,7 +326,14 @@ UTILS_LOGGER_LEVELS: Dict[str, str] = {
|
|||
|
||||
|
||||
X_FRAME_OPTIONS = 'SAMEORIGIN'
|
||||
CSRF_TRUSTED_ORIGINS = ['ietf.org', '*.ietf.org', 'meetecho.com', '*.meetecho.com', 'gather.town', '*.gather.town', ]
|
||||
CSRF_TRUSTED_ORIGINS = [
|
||||
"https://ietf.org",
|
||||
"https://*.ietf.org",
|
||||
'https://meetecho.com',
|
||||
'https://*.meetecho.com',
|
||||
'https://gather.town',
|
||||
'https://*.gather.town',
|
||||
]
|
||||
CSRF_COOKIE_SAMESITE = 'None'
|
||||
CSRF_COOKIE_SECURE = True
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import datetime
|
||||
|
||||
from decorator import decorator, decorate
|
||||
from functools import wraps
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import login
|
||||
|
@ -39,52 +40,54 @@ def person_required(f, request, *args, **kwargs):
|
|||
return render(request, 'registration/missing_person.html')
|
||||
return f(request, *args, **kwargs)
|
||||
|
||||
@decorator
|
||||
def require_api_key(f, request, *args, **kwargs):
|
||||
|
||||
def err(code, text):
|
||||
return HttpResponse(text, status=code, content_type='text/plain')
|
||||
# Check method and get hash
|
||||
if request.method == 'POST':
|
||||
hash = request.POST.get('apikey')
|
||||
elif request.method == 'GET':
|
||||
hash = request.GET.get('apikey')
|
||||
else:
|
||||
return err(405, "Method not allowed")
|
||||
if not hash:
|
||||
return err(400, "Missing apikey parameter")
|
||||
# Check hash
|
||||
key = PersonalApiKey.validate_key(force_bytes(hash))
|
||||
if not key:
|
||||
return err(403, "Invalid apikey")
|
||||
# Check endpoint
|
||||
urlpath = request.META.get('PATH_INFO')
|
||||
if not (urlpath and urlpath == key.endpoint):
|
||||
return err(400, "Apikey endpoint mismatch")
|
||||
# Check time since regular login
|
||||
person = key.person
|
||||
last_login = person.user.last_login
|
||||
if not person.user.is_staff:
|
||||
time_limit = (timezone.now() - datetime.timedelta(days=settings.UTILS_APIKEY_GUI_LOGIN_LIMIT_DAYS))
|
||||
if last_login == None or last_login < time_limit:
|
||||
return err(400, "Too long since last regular login")
|
||||
# Log in
|
||||
login(request, person.user)
|
||||
# restore the user.last_login field, so it reflects only gui logins
|
||||
person.user.last_login = last_login
|
||||
person.user.save()
|
||||
# Update stats
|
||||
key.count += 1
|
||||
key.latest = timezone.now()
|
||||
key.save()
|
||||
PersonApiKeyEvent.objects.create(person=person, type='apikey_login', key=key, desc="Logged in with key ID %s, endpoint %s" % (key.id, key.endpoint))
|
||||
# Execute decorated function
|
||||
try:
|
||||
ret = f(request, *args, **kwargs)
|
||||
except AttributeError as e:
|
||||
log.log("Bad API call: args: %s, kwargs: %s, exception: %s" % (args, kwargs, e))
|
||||
return err(400, "Bad or missing parameters")
|
||||
return ret
|
||||
|
||||
def require_api_key(f):
|
||||
@wraps(f)
|
||||
def _wrapper(request, *args, **kwargs):
|
||||
def err(code, text):
|
||||
return HttpResponse(text, status=code, content_type='text/plain')
|
||||
# Check method and get hash
|
||||
if request.method == 'POST':
|
||||
hash = request.POST.get('apikey')
|
||||
elif request.method == 'GET':
|
||||
hash = request.GET.get('apikey')
|
||||
else:
|
||||
return err(405, "Method not allowed")
|
||||
if not hash:
|
||||
return err(400, "Missing apikey parameter")
|
||||
# Check hash
|
||||
key = PersonalApiKey.validate_key(force_bytes(hash))
|
||||
if not key:
|
||||
return err(403, "Invalid apikey")
|
||||
# Check endpoint
|
||||
urlpath = request.META.get('PATH_INFO')
|
||||
if not (urlpath and urlpath == key.endpoint):
|
||||
return err(400, "Apikey endpoint mismatch")
|
||||
# Check time since regular login
|
||||
person = key.person
|
||||
last_login = person.user.last_login
|
||||
if not person.user.is_staff:
|
||||
time_limit = (timezone.now() - datetime.timedelta(days=settings.UTILS_APIKEY_GUI_LOGIN_LIMIT_DAYS))
|
||||
if last_login == None or last_login < time_limit:
|
||||
return err(400, "Too long since last regular login")
|
||||
# Log in
|
||||
login(request, person.user)
|
||||
# restore the user.last_login field, so it reflects only gui logins
|
||||
person.user.last_login = last_login
|
||||
person.user.save()
|
||||
# Update stats
|
||||
key.count += 1
|
||||
key.latest = timezone.now()
|
||||
key.save()
|
||||
PersonApiKeyEvent.objects.create(person=person, type='apikey_login', key=key, desc="Logged in with key ID %s, endpoint %s" % (key.id, key.endpoint))
|
||||
# Execute decorated function
|
||||
try:
|
||||
ret = f(request, *args, **kwargs)
|
||||
except AttributeError as e:
|
||||
log.log("Bad API call: args: %s, kwargs: %s, exception: %s" % (args, kwargs, e))
|
||||
return err(400, "Bad or missing parameters")
|
||||
return ret
|
||||
return _wrapper
|
||||
|
||||
|
||||
def _memoize(func, self, *args, **kwargs):
|
||||
|
|
|
@ -74,7 +74,7 @@ from django.urls import URLResolver # type: ignore
|
|||
from django.template.backends.django import DjangoTemplates
|
||||
from django.template.backends.django import Template # type: ignore[attr-defined]
|
||||
from django.utils import timezone
|
||||
# from django.utils.safestring import mark_safe
|
||||
from django.views.generic import RedirectView, TemplateView
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
debug.debug = True
|
||||
|
@ -550,8 +550,10 @@ class CoverageTest(unittest.TestCase):
|
|||
return (regex in ("^_test500/$", "^accounts/testemail/$")
|
||||
or regex.startswith("^admin/")
|
||||
or re.search('^api/v1/[^/]+/[^/]+/', regex)
|
||||
or getattr(pattern.callback, "__name__", "") == "RedirectView"
|
||||
or getattr(pattern.callback, "__name__", "") == "TemplateView"
|
||||
or (
|
||||
hasattr(pattern.callback, "view_class")
|
||||
and issubclass(pattern.callback.view_class, (RedirectView, TemplateView))
|
||||
)
|
||||
or pattern.callback == django.views.static.serve)
|
||||
|
||||
patterns = [(regex, re.compile(regex, re.U), obj) for regex, obj in url_patterns
|
||||
|
|
|
@ -11,7 +11,9 @@ from django.utils.encoding import force_str
|
|||
from django.views.generic import View
|
||||
|
||||
def url(regex, view, kwargs=None, name=None):
|
||||
if callable(view) and hasattr(view, '__name__'):
|
||||
if hasattr(view, "view_class"):
|
||||
view_name = "%s.%s" % (view.__module__, view.view_class.__name__)
|
||||
elif callable(view) and hasattr(view, '__name__'):
|
||||
view_name = "%s.%s" % (view.__module__, view.__name__)
|
||||
else:
|
||||
view_name = regex
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
--- django/contrib/messages/storage/cookie.py.orig 2020-08-13 11:10:36.719177122 +0200
|
||||
+++ django/contrib/messages/storage/cookie.py 2020-08-13 11:45:23.503463150 +0200
|
||||
@@ -108,6 +108,8 @@
|
||||
@@ -109,6 +109,8 @@
|
||||
response.delete_cookie(
|
||||
self.cookie_name,
|
||||
domain=settings.SESSION_COOKIE_DOMAIN,
|
||||
|
@ -11,39 +11,42 @@
|
|||
|
||||
--- django/http/response.py.orig 2020-08-13 11:16:04.060627793 +0200
|
||||
+++ django/http/response.py 2020-08-13 11:54:03.482476973 +0200
|
||||
@@ -243,12 +243,18 @@
|
||||
@@ -261,20 +261,28 @@
|
||||
value = signing.get_cookie_signer(salt=key + salt).sign(value)
|
||||
return self.set_cookie(key, value, **kwargs)
|
||||
|
||||
- def delete_cookie(self, key, path='/', domain=None, samesite=None):
|
||||
+ def delete_cookie(self, key, path='/', domain=None, secure=False, httponly=False, samesite=None):
|
||||
|
||||
- def delete_cookie(self, key, path="/", domain=None, samesite=None):
|
||||
+ def delete_cookie(self, key, path="/", domain=None, secure=False, httponly=False, samesite=None):
|
||||
# Browsers can ignore the Set-Cookie header if the cookie doesn't use
|
||||
# the secure flag and:
|
||||
# - the cookie name starts with "__Host-" or "__Secure-", or
|
||||
# - the samesite is "none".
|
||||
- secure = (
|
||||
- key.startswith(('__Secure-', '__Host-')) or
|
||||
- (samesite and samesite.lower() == 'none')
|
||||
- secure = key.startswith(("__Secure-", "__Host-")) or (
|
||||
- samesite and samesite.lower() == "none"
|
||||
- )
|
||||
+ if key in self.cookies:
|
||||
+ domain = self.cookies[key].get('domain', domain)
|
||||
+ secure = self.cookies[key].get('secure', secure)
|
||||
+ httponly = self.cookies[key].get('httponly', httponly)
|
||||
+ samesite = self.cookies[key].get('samesite', samesite)
|
||||
+ domain = self.cookies[key].get("domain", domain)
|
||||
+ secure = self.cookies[key].get("secure", secure)
|
||||
+ httponly = self.cookies[key].get("httponly", httponly)
|
||||
+ samesite = self.cookies[key].get("samesite", samesite)
|
||||
+ else:
|
||||
+ secure = secure or (
|
||||
+ key.startswith(('__Secure-', '__Host-')) or
|
||||
+ (samesite and samesite.lower() == 'none')
|
||||
+ key.startswith(("__Secure-", "__Host-")) or
|
||||
+ (samesite and samesite.lower() == "none")
|
||||
+ )
|
||||
self.set_cookie(
|
||||
- key, max_age=0, path=path, domain=domain, secure=secure,
|
||||
+ key, max_age=0, path=path, domain=domain, secure=secure, httponly=httponly,
|
||||
expires='Thu, 01 Jan 1970 00:00:00 GMT', samesite=samesite,
|
||||
key,
|
||||
max_age=0,
|
||||
path=path,
|
||||
domain=domain,
|
||||
secure=secure,
|
||||
+ httponly=httponly,
|
||||
expires="Thu, 01 Jan 1970 00:00:00 GMT",
|
||||
samesite=samesite,
|
||||
)
|
||||
|
||||
--- django/contrib/sessions/middleware.py.orig 2020-08-13 12:12:12.401898114 +0200
|
||||
+++ django/contrib/sessions/middleware.py 2020-08-13 12:14:52.690520659 +0200
|
||||
@@ -40,6 +40,8 @@
|
||||
@@ -38,6 +38,8 @@
|
||||
settings.SESSION_COOKIE_NAME,
|
||||
path=settings.SESSION_COOKIE_PATH,
|
||||
domain=settings.SESSION_COOKIE_DOMAIN,
|
||||
|
@ -51,4 +54,4 @@
|
|||
+ httponly=settings.SESSION_COOKIE_HTTPONLY or None,
|
||||
samesite=settings.SESSION_COOKIE_SAMESITE,
|
||||
)
|
||||
patch_vary_headers(response, ('Cookie',))
|
||||
patch_vary_headers(response, ("Cookie",))
|
||||
|
|
|
@ -11,7 +11,7 @@ coverage>=4.5.4,<5.0 # Coverage 5.x moves from a json database to SQLite. Mo
|
|||
decorator>=5.1.1
|
||||
types-decorator>=5.1.1
|
||||
defusedxml>=0.7.1 # for TastyPie when using xml; not a declared dependency
|
||||
Django<4
|
||||
Django<4.1
|
||||
django-analytical>=3.1.0
|
||||
django-bootstrap5>=21.3
|
||||
django-celery-beat>=2.3.0
|
||||
|
|
Loading…
Reference in a new issue