From a1a3062a6fa476d3e93a41d234a4b25a54c27780 Mon Sep 17 00:00:00 2001 From: Balthazar Rouberol Date: Wed, 1 Nov 2023 16:39:59 +0100 Subject: [PATCH 1/2] Resolve deprecation warnings related to datetime.utcnow in python 3.12 --- jose/jwt.py | 6 +++--- jose/utils.py | 13 +++++++++++++ tests/test_jwt.py | 23 ++++++++++++----------- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/jose/jwt.py b/jose/jwt.py index b364b4ba..8972711f 100644 --- a/jose/jwt.py +++ b/jose/jwt.py @@ -11,7 +11,7 @@ from .constants import ALGORITHMS from .exceptions import ExpiredSignatureError, JWSError, JWTClaimsError, JWTError -from .utils import calculate_at_hash, timedelta_total_seconds +from .utils import calculate_at_hash, timedelta_total_seconds, utcnow def encode(claims, key, algorithm=ALGORITHMS.HS256, headers=None, access_token=None): @@ -281,7 +281,7 @@ def _validate_nbf(claims, leeway=0): except ValueError: raise JWTClaimsError("Not Before claim (nbf) must be an integer.") - now = timegm(datetime.utcnow().utctimetuple()) + now = timegm(utcnow().utctimetuple()) if nbf > (now + leeway): raise JWTClaimsError("The token is not yet valid (nbf)") @@ -311,7 +311,7 @@ def _validate_exp(claims, leeway=0): except ValueError: raise JWTClaimsError("Expiration Time claim (exp) must be an integer.") - now = timegm(datetime.utcnow().utctimetuple()) + now = timegm(utcnow().utctimetuple()) if exp < (now - leeway): raise ExpiredSignatureError("Signature has expired.") diff --git a/jose/utils.py b/jose/utils.py index d04c4ac0..e93d49ea 100644 --- a/jose/utils.py +++ b/jose/utils.py @@ -1,4 +1,5 @@ import base64 +import datetime import struct # Piggyback of the backends implementation of the function that converts a long @@ -105,3 +106,15 @@ def ensure_binary(s): if isinstance(s, str): return s.encode("utf-8", "strict") raise TypeError(f"not expecting type '{type(s)}'") + + + +def utcnow(): + try: + from datetime import UTC + except ImportError: + # datetime.datetime.utcnow was deprecated in favor of + # datetime.datetime.now(datetime.UTC) in python 3.12 + return datetime.datetime.utcnow() + else: + return datetime.datetime.now(datetime.UTC) diff --git a/tests/test_jwt.py b/tests/test_jwt.py index 8c2e262f..9c010cbe 100644 --- a/tests/test_jwt.py +++ b/tests/test_jwt.py @@ -6,6 +6,7 @@ from jose import jws, jwt from jose.exceptions import JWTError +from jose.utils import utcnow @pytest.fixture @@ -180,7 +181,7 @@ def test_leeway_is_int(self): pass def test_leeway_is_timedelta(self, claims, key): - nbf = datetime.utcnow() + timedelta(seconds=5) + nbf = utcnow() + timedelta(seconds=5) leeway = timedelta(seconds=10) claims = { @@ -209,7 +210,7 @@ def test_nbf_not_int(self, key): jwt.decode(token, key) def test_nbf_datetime(self, key): - nbf = datetime.utcnow() - timedelta(seconds=5) + nbf = utcnow() - timedelta(seconds=5) claims = {"nbf": nbf} @@ -217,7 +218,7 @@ def test_nbf_datetime(self, key): jwt.decode(token, key) def test_nbf_with_leeway(self, key): - nbf = datetime.utcnow() + timedelta(seconds=5) + nbf = utcnow() + timedelta(seconds=5) claims = { "nbf": nbf, @@ -229,7 +230,7 @@ def test_nbf_with_leeway(self, key): jwt.decode(token, key, options=options) def test_nbf_in_future(self, key): - nbf = datetime.utcnow() + timedelta(seconds=5) + nbf = utcnow() + timedelta(seconds=5) claims = {"nbf": nbf} @@ -239,7 +240,7 @@ def test_nbf_in_future(self, key): jwt.decode(token, key) def test_nbf_skip(self, key): - nbf = datetime.utcnow() + timedelta(seconds=5) + nbf = utcnow() + timedelta(seconds=5) claims = {"nbf": nbf} @@ -261,7 +262,7 @@ def test_exp_not_int(self, key): jwt.decode(token, key) def test_exp_datetime(self, key): - exp = datetime.utcnow() + timedelta(seconds=5) + exp = utcnow() + timedelta(seconds=5) claims = {"exp": exp} @@ -269,7 +270,7 @@ def test_exp_datetime(self, key): jwt.decode(token, key) def test_exp_with_leeway(self, key): - exp = datetime.utcnow() - timedelta(seconds=5) + exp = utcnow() - timedelta(seconds=5) claims = { "exp": exp, @@ -281,7 +282,7 @@ def test_exp_with_leeway(self, key): jwt.decode(token, key, options=options) def test_exp_in_past(self, key): - exp = datetime.utcnow() - timedelta(seconds=5) + exp = utcnow() - timedelta(seconds=5) claims = {"exp": exp} @@ -291,7 +292,7 @@ def test_exp_in_past(self, key): jwt.decode(token, key) def test_exp_skip(self, key): - exp = datetime.utcnow() - timedelta(seconds=5) + exp = utcnow() - timedelta(seconds=5) claims = {"exp": exp} @@ -504,8 +505,8 @@ def test_unverified_claims_object(self, claims, key): [ ("aud", "aud"), ("ait", "ait"), - ("exp", datetime.utcnow() + timedelta(seconds=3600)), - ("nbf", datetime.utcnow() - timedelta(seconds=5)), + ("exp", utcnow() + timedelta(seconds=3600)), + ("nbf", utcnow() - timedelta(seconds=5)), ("iss", "iss"), ("sub", "sub"), ("jti", "jti"), From f29f15080f9fee453a559f1b9d18dbff8715dc66 Mon Sep 17 00:00:00 2001 From: Balthazar Rouberol Date: Wed, 27 Dec 2023 13:43:58 +0100 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Aliaksei Urbanski --- jose/utils.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/jose/utils.py b/jose/utils.py index e93d49ea..03f96f6b 100644 --- a/jose/utils.py +++ b/jose/utils.py @@ -1,6 +1,6 @@ import base64 -import datetime import struct +from datetime import datetime, timezone # Piggyback of the backends implementation of the function that converts a long # to a bytes stream. Some plumbing is necessary to have the signatures match. @@ -108,13 +108,6 @@ def ensure_binary(s): raise TypeError(f"not expecting type '{type(s)}'") - def utcnow(): - try: - from datetime import UTC - except ImportError: - # datetime.datetime.utcnow was deprecated in favor of - # datetime.datetime.now(datetime.UTC) in python 3.12 - return datetime.datetime.utcnow() - else: - return datetime.datetime.now(datetime.UTC) + # datetime.utcnow() is deprecated since Python 3.12 + return datetime.now(timezone.utc)