From 7d98a49bc38d0849367b348bfe37a2b689323419 Mon Sep 17 00:00:00 2001 From: Sha 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)