From e414f3b8fd46be99b2dfe4b0fb750086cb73271a Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 13 Nov 2023 09:15:12 -0700 Subject: [PATCH] fix(auth): fix timezone issues with recovery tokens --- selfprivacy_api/utils/timeutils.py | 4 +++- tests/common.py | 4 ++++ tests/test_graphql/api_common.py | 12 ++++++------ tests/test_graphql/test_api_recovery.py | 12 ++++++------ tests/test_rest_endpoints/test_auth.py | 6 +++--- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/selfprivacy_api/utils/timeutils.py b/selfprivacy_api/utils/timeutils.py index 36871c3..b6494c6 100644 --- a/selfprivacy_api/utils/timeutils.py +++ b/selfprivacy_api/utils/timeutils.py @@ -7,7 +7,9 @@ def ensure_tz_aware(dt: datetime) -> datetime: assumes utc on naive datetime input """ if dt.tzinfo is None: - dt = dt.astimezone(timezone.utc) + # astimezone() is dangerous, it makes an implicit assumption that + # the time is localtime + dt = dt.replace(tzinfo=timezone.utc) return dt diff --git a/tests/common.py b/tests/common.py index c327ae9..5199899 100644 --- a/tests/common.py +++ b/tests/common.py @@ -36,6 +36,10 @@ class NearFuture(datetime): def now(cls, tz=None): return datetime.now(tz) + timedelta(minutes=13) + @classmethod + def utcnow(cls): + return datetime.utcnow() + timedelta(minutes=13) + def read_json(file_path): with open(file_path, "r", encoding="utf-8") as file: diff --git a/tests/test_graphql/api_common.py b/tests/test_graphql/api_common.py index bfac767..4e4aec2 100644 --- a/tests/test_graphql/api_common.py +++ b/tests/test_graphql/api_common.py @@ -6,16 +6,16 @@ ORIGINAL_DEVICES = TOKENS_FILE_CONTENTS["tokens"] def assert_ok(response, request): data = assert_data(response) - data[request]["success"] is True - data[request]["message"] is not None - data[request]["code"] == 200 + assert data[request]["success"] is True + assert data[request]["message"] is not None + assert data[request]["code"] == 200 def assert_errorcode(response, request, code): data = assert_data(response) - data[request]["success"] is False - data[request]["message"] is not None - data[request]["code"] == code + assert data[request]["success"] is False + assert data[request]["message"] is not None + assert data[request]["code"] == code def assert_empty(response): diff --git a/tests/test_graphql/test_api_recovery.py b/tests/test_graphql/test_api_recovery.py index b0155e7..629bac0 100644 --- a/tests/test_graphql/test_api_recovery.py +++ b/tests/test_graphql/test_api_recovery.py @@ -177,9 +177,9 @@ def test_graphql_generate_recovery_key_with_expiration_date( assert_recovery_recent(status["creationDate"]) # timezone-aware comparison. Should pass regardless of server's tz - assert datetime.fromisoformat( - status["expirationDate"] - ) == expiration_date.astimezone(timezone.utc) + assert datetime.fromisoformat(status["expirationDate"]) == expiration_date.replace( + tzinfo=timezone.utc + ) assert status["usesLeft"] is None @@ -208,9 +208,9 @@ def test_graphql_use_recovery_key_after_expiration( assert_recovery_recent(status["creationDate"]) # timezone-aware comparison. Should pass regardless of server's tz - assert datetime.fromisoformat( - status["expirationDate"] - ) == expiration_date.astimezone(timezone.utc) + assert datetime.fromisoformat(status["expirationDate"]) == expiration_date.replace( + tzinfo=timezone.utc + ) assert status["usesLeft"] is None diff --git a/tests/test_rest_endpoints/test_auth.py b/tests/test_rest_endpoints/test_auth.py index 8565143..4d0d2ed 100644 --- a/tests/test_rest_endpoints/test_auth.py +++ b/tests/test_rest_endpoints/test_auth.py @@ -12,8 +12,8 @@ from tests.common import ( NearFuture, assert_recovery_recent, ) -from tests.common import five_minutes_into_future_naive as five_minutes_into_future -from tests.common import five_minutes_into_past_naive as five_minutes_into_past +from tests.common import five_minutes_into_future_naive_utc as five_minutes_into_future +from tests.common import five_minutes_into_past_naive_utc as five_minutes_into_past DATE_FORMATS = [ "%Y-%m-%dT%H:%M:%S.%fZ", @@ -338,7 +338,7 @@ def test_generate_recovery_token_with_expiration_date( "exists": True, "valid": True, "date": time_generated, - "expiration": expiration_date.astimezone(timezone.utc).isoformat(), + "expiration": expiration_date.replace(tzinfo=timezone.utc).isoformat(), "uses_left": None, }