python3-Flask-Login: patch for Flask and Werzeug 3

This commit is contained in:
Andrew J. Hesford 2023-10-16 15:50:49 -04:00
parent 66462c6e00
commit c784c6937d
3 changed files with 366 additions and 2 deletions

View File

@ -0,0 +1,46 @@
From 7b170bf4e5e0240fe084166c9ca6ec4fba258dcd Mon Sep 17 00:00:00 2001
From: Hiromasa Ihara <iharahiromasa@gmail.com>
Date: Mon, 2 Oct 2023 20:14:40 +0900
Subject: [PATCH] fix: avoid DeprecationWarning 'werkzeug.urls.url_decode' is
deprecated (#746)
---
src/flask_login/utils.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/flask_login/utils.py b/src/flask_login/utils.py
index c3c46846..45a7e279 100644
--- a/src/flask_login/utils.py
+++ b/src/flask_login/utils.py
@@ -3,6 +3,8 @@
from hashlib import sha512
from urllib.parse import urlparse
from urllib.parse import urlunparse
+from urllib.parse import parse_qs
+from urllib.parse import urlencode
from flask import current_app
from flask import g
@@ -11,8 +13,6 @@
from flask import session
from flask import url_for
from werkzeug.local import LocalProxy
-from werkzeug.urls import url_decode
-from werkzeug.urls import url_encode
from .config import COOKIE_NAME
from .config import EXEMPT_METHODS
@@ -123,11 +123,11 @@ def login_url(login_view, next_url=None, next_field="next"):
return base
parsed_result = urlparse(base)
- md = url_decode(parsed_result.query)
+ md = parse_qs(parsed_result.query)
md[next_field] = make_next_param(base, next_url)
netloc = current_app.config.get("FORCE_HOST_FOR_REDIRECTS") or parsed_result.netloc
parsed_result = parsed_result._replace(
- netloc=netloc, query=url_encode(md, sort=True)
+ netloc=netloc, query=urlencode(md, doseq=True)
)
return urlunparse(parsed_result)

View File

@ -0,0 +1,317 @@
From 7d98a49bc38d0849367b348bfe37a2b689323419 Mon Sep 17 00:00:00 2001
From: Sha <wangsha@users.noreply.github.com>
Date: Mon, 2 Oct 2023 07:00:12 -0500
Subject: [PATCH] flask 3.0 compatibility (#778)
* Werkzeug 3.0 compatible
* python3.7 compatibility
* troubleshooting version compatibility
* update constrain
* package version troubleshooting
* troubleshooting package version
* troubleshoot package version
* package tuning
* troubleshoot package version
* troubleshooting package version
* troubleshoot test cases
* troubleshoot python package version
* Update tox.ini
* version troubleshoot
* version fix
* package version
* package version
* Update setup.py
* Update setup.py
* package version
* package version
* package version
* Update setup.cfg
* version fix
* package version
* package version
* package version
* package version
* deprecate python3.7
* package version
* merge conflicts
* pylint fix
---
.github/workflows/tests.yaml | 4 +-
requirements/ci-release.txt | 83 +++++++++------------
requirements/ci-tests.in | 5 +-
requirements/ci-tests.txt | 61 +++++++++-------
requirements/dev.txt | 138 ++++++-----------------------------
requirements/docs.txt | 50 +++++++------
requirements/style.txt | 25 +++----
requirements/tests-min.in | 8 +-
requirements/tests-min.txt | 34 ++++-----
requirements/tests.in | 6 +-
requirements/tests.txt | 28 +++----
setup.cfg | 2 +-
setup.py | 5 +-
src/flask_login/utils.py | 4 +-
tests/test_login.py | 26 ++++---
tox.ini | 2 +-
16 files changed, 198 insertions(+), 283 deletions(-)
diff --git a/src/flask_login/utils.py b/src/flask_login/utils.py
index 45a7e279..0fde23f4 100644
--- a/src/flask_login/utils.py
+++ b/src/flask_login/utils.py
@@ -1,10 +1,10 @@
import hmac
from functools import wraps
from hashlib import sha512
-from urllib.parse import urlparse
-from urllib.parse import urlunparse
from urllib.parse import parse_qs
from urllib.parse import urlencode
+from urllib.parse import urlparse
+from urllib.parse import urlunparse
from flask import current_app
from flask import g
diff --git a/tests/test_login.py b/tests/test_login.py
--- a/tests/test_login.py
+++ b/tests/test_login.py
@@ -328,7 +328,7 @@
def empty_session():
return f"modified={session.modified}"
- # This will help us with the possibility of typoes in the tests. Now
+ # This will help us with the possibility of typos in the tests. Now
# we shouldn't have to check each response to help us set up state
# (such as login pages) to make sure it worked: we will always
# get an exception raised (rather than return a 404 response)
@@ -669,24 +669,17 @@
name = self.app.config["REMEMBER_COOKIE_NAME"] = "myname"
duration = self.app.config["REMEMBER_COOKIE_DURATION"] = timedelta(days=2)
path = self.app.config["REMEMBER_COOKIE_PATH"] = "/mypath"
- domain = self.app.config["REMEMBER_COOKIE_DOMAIN"] = ".localhost.local"
+ domain = self.app.config["REMEMBER_COOKIE_DOMAIN"] = "localhost.local"
with self.app.test_client() as c:
c.get("/login-notch-remember")
- # TODO: Is there a better way to test this?
- self.assertIn(
- domain,
- c.cookie_jar._cookies,
- "Custom domain not found as cookie domain",
+ cookie = c.get_cookie(key=name, domain=domain, path=path)
+ self.assertIsNotNone(
+ cookie, "Custom domain, path and name not found in cookies"
)
- domain_cookie = c.cookie_jar._cookies[domain]
- self.assertIn(path, domain_cookie, "Custom path not found as cookie path")
- path_cookie = domain_cookie[path]
- self.assertIn(name, path_cookie, "Custom name not found as cookie name")
- cookie = path_cookie[name]
- expiration_date = datetime.utcfromtimestamp(cookie.expires)
+ expiration_date = datetime.utcfromtimestamp(cookie.expires.timestamp())
expected_date = datetime.utcnow() + duration
difference = expected_date - expiration_date
@@ -702,24 +695,17 @@
self.app.config["REMEMBER_COOKIE_DURATION"] = 172800
duration = timedelta(hours=7)
path = self.app.config["REMEMBER_COOKIE_PATH"] = "/mypath"
- domain = self.app.config["REMEMBER_COOKIE_DOMAIN"] = ".localhost.local"
+ domain = self.app.config["REMEMBER_COOKIE_DOMAIN"] = "localhost.local"
with self.app.test_client() as c:
c.get("/login-notch-remember-custom")
- # TODO: Is there a better way to test this?
- self.assertIn(
- domain,
- c.cookie_jar._cookies,
- "Custom domain not found as cookie domain",
+ cookie = c.get_cookie(key=name, domain=domain, path=path)
+ self.assertIsNotNone(
+ cookie, "Custom domain, path and name not found in cookies"
)
- domain_cookie = c.cookie_jar._cookies[domain]
- self.assertIn(path, domain_cookie, "Custom path not found as cookie path")
- path_cookie = domain_cookie[path]
- self.assertIn(name, path_cookie, "Custom name not found as cookie name")
- cookie = path_cookie[name]
- expiration_date = datetime.utcfromtimestamp(cookie.expires)
+ expiration_date = datetime.utcfromtimestamp(cookie.expires.timestamp())
expected_date = datetime.utcnow() + duration
difference = expected_date - expiration_date
@@ -734,15 +720,15 @@
self.app.config["REMEMBER_COOKIE_DURATION"] = 172800
duration = timedelta(seconds=172800)
name = self.app.config["REMEMBER_COOKIE_NAME"] = "myname"
- domain = self.app.config["REMEMBER_COOKIE_DOMAIN"] = ".localhost.local"
+ domain = self.app.config["REMEMBER_COOKIE_DOMAIN"] = "localhost.local"
with self.app.test_client() as c:
result = c.get("/login-notch-remember")
self.assertEqual(result.status_code, 200)
- cookie = c.cookie_jar._cookies[domain]["/"][name]
+ cookie = c.get_cookie(key=name, domain=domain, path="/")
- expiration_date = datetime.utcfromtimestamp(cookie.expires)
+ expiration_date = datetime.utcfromtimestamp(cookie.expires.timestamp())
expected_date = datetime.utcnow() + duration
difference = expected_date - expiration_date
@@ -794,25 +780,22 @@
self.assertIn(expected_exception_message, str(cm.exception))
def test_remember_me_refresh_every_request(self):
- domain = self.app.config["REMEMBER_COOKIE_DOMAIN"] = ".localhost.local"
+ domain = self.app.config["REMEMBER_COOKIE_DOMAIN"] = "localhost.local"
path = self.app.config["REMEMBER_COOKIE_PATH"] = "/"
# No refresh
self.app.config["REMEMBER_COOKIE_REFRESH_EACH_REQUEST"] = False
with self.app.test_client() as c:
c.get("/login-notch-remember")
- self.assertIn("remember", c.cookie_jar._cookies[domain][path])
- expiration_date_1 = datetime.utcfromtimestamp(
- c.cookie_jar._cookies[domain][path]["remember"].expires
- )
-
- self._delete_session(c)
+ cookie = c.get_cookie(key="remember", domain=domain, path=path)
+ self.assertIsNotNone(cookie)
+ expiration_date_1 = datetime.utcfromtimestamp(cookie.expires.timestamp())
+ # self._delete_session(c)
c.get("/username")
- self.assertIn("remember", c.cookie_jar._cookies[domain][path])
- expiration_date_2 = datetime.utcfromtimestamp(
- c.cookie_jar._cookies[domain][path]["remember"].expires
- )
+ cookie = c.get_cookie(key="remember", domain=domain, path=path)
+ self.assertIsNotNone(cookie)
+ expiration_date_2 = datetime.utcfromtimestamp(cookie.expires.timestamp())
self.assertEqual(expiration_date_1, expiration_date_2)
# With refresh (mock datetime's `utcnow`)
@@ -820,22 +803,24 @@
self.app.config["REMEMBER_COOKIE_REFRESH_EACH_REQUEST"] = True
now = datetime.utcnow()
mock_dt.utcnow = Mock(return_value=now)
-
+ mock_utcnow1 = mock_dt.utcnow
with self.app.test_client() as c:
c.get("/login-notch-remember")
- self.assertIn("remember", c.cookie_jar._cookies[domain][path])
+ cookie = c.get_cookie(key="remember", domain=domain, path=path)
expiration_date_1 = datetime.utcfromtimestamp(
- c.cookie_jar._cookies[domain][path]["remember"].expires
+ cookie.expires.timestamp()
)
self.assertIsNotNone(expiration_date_1)
- self._delete_session(c)
+ # self._delete_session(c)
mock_dt.utcnow = Mock(return_value=now + timedelta(seconds=1))
+ mock_utcnow2 = mock_dt.utcnow
+ self.assertNotEqual(mock_utcnow1, mock_utcnow2)
c.get("/username")
- self.assertIn("remember", c.cookie_jar._cookies[domain][path])
+ cookie = c.get_cookie(key="remember", domain=domain, path=path)
expiration_date_2 = datetime.utcfromtimestamp(
- c.cookie_jar._cookies[domain][path]["remember"].expires
+ cookie.expires.timestamp()
)
self.assertIsNotNone(expiration_date_2)
self.assertNotEqual(expiration_date_1, expiration_date_2)
@@ -1016,7 +1001,7 @@
c.get("/login-notch-remember")
with c.session_transaction() as sess:
sess["_user_id"] = None
- c.set_cookie(domain, self.remember_cookie_name, "foo")
+ c.set_cookie(self.remember_cookie_name, "foo", domain=domain)
result = c.get("/username")
self.assertEqual("Anonymous", result.data.decode("utf-8"))
@@ -1315,7 +1300,7 @@
pass
return USERS.get(user_id)
- # This will help us with the possibility of typoes in the tests. Now
+ # This will help us with the possibility of typos in the tests. Now
# we shouldn't have to check each response to help us set up state
# (such as login pages) to make sure it worked: we will always
# get an exception raised (rather than return a 404 response)
@@ -1426,9 +1411,10 @@
result = login_url("https://auth.localhost/login", PROTECTED)
self.assertEqual(expected, result)
+ url = login_url("/login?affil=cgnu", PROTECTED)
self.assertEqual(
"/login?affil=cgnu&next=%2Fprotected",
- login_url("/login?affil=cgnu", PROTECTED),
+ url,
)
def test_login_url_generation_with_view(self):
@@ -1590,7 +1576,7 @@
def load_user(user_id):
return USERS[str(user_id)]
- # This will help us with the possibility of typoes in the tests. Now
+ # This will help us with the possibility of typos in the tests. Now
# we shouldn't have to check each response to help us set up state
# (such as login pages) to make sure it worked: we will always
# get an exception raised (rather than return a 404 response)
@@ -1646,7 +1632,7 @@
def load_user(user_id):
return USERS[str(user_id)]
- # This will help us with the possibility of typoes in the tests. Now
+ # This will help us with the possibility of typos in the tests. Now
# we shouldn't have to check each response to help us set up state
# (such as login pages) to make sure it worked: we will always
# get an exception raised (rather than return a 404 response)
@@ -1742,7 +1728,7 @@
def load_user(user_id):
return USERS[int(user_id)]
- # This will help us with the possibility of typoes in the tests. Now
+ # This will help us with the possibility of typos in the tests. Now
# we shouldn't have to check each response to help us set up state
# (such as login pages) to make sure it worked: we will always
# get an exception raised (rather than return a 404 response)

View File

@ -1,11 +1,12 @@
# Template file for 'python3-Flask-Login'
pkgname=python3-Flask-Login
version=0.6.2
revision=3
revision=4
build_style=python3-module
make_check_args="-p no:warnings"
hostmakedepends="python3-setuptools"
depends="python3-Flask"
checkdepends="python3-Werkzeug python3-Flask"
checkdepends="python3-semanticversion python3-pytest python3-asgiref $depends"
short_desc="User session management for Flask (Python3)"
maintainer="pulux <pulux@pf4sh.de>"
license="MIT"