| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 3f88a24 commit e431f20
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -124,6 +124,26 @@ def utcnow(): | |||
| 124 | 124 | return now | |
| 125 | 125 | ||
| 126 | 126 | ||
| 127 | + def utcfromtimestamp(timestamp): | ||
| 128 | + """Returns the UTC datetime from a timestamp. | ||
| 129 | + | ||
| 130 | + Args: | ||
| 131 | + timestamp (float): The timestamp to convert. | ||
| 132 | + | ||
| 133 | + Returns: | ||
| 134 | + datetime: The time in UTC. | ||
| 135 | + """ | ||
| 136 | + # We used datetime.utcfromtimestamp() before, since it's deprecated from | ||
| 137 | + # python 3.12, we are using datetime.fromtimestamp(timestamp, timezone.utc) | ||
| 138 | + # now. "utcfromtimestamp()" is offset-native (no timezone info), but | ||
| 139 | + # "fromtimestamp(timestamp, timezone.utc)" is offset-aware (with timezone | ||
| 140 | + # info). This will cause datetime comparison problem. For backward | ||
| 141 | + # compatibility, we need to remove the timezone info. | ||
| 142 | + dt = datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc) | ||
| 143 | + dt = dt.replace(tzinfo=None) | ||
| 144 | + return dt | ||
| 145 | + | ||
| 146 | + | ||
| 127 | 147 | def datetime_to_secs(value): | |
| 128 | 148 | """Convert a datetime object to the number of seconds since the UNIX epoch. | |
| 129 | 149 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,7 +22,6 @@ | |||
| 22 | 22 | https://cloud.google.com/appengine/docs/python/appidentity/ | |
| 23 | 23 | """ | |
| 24 | 24 | ||
| 25 | - import datetime | ||
| 26 | 25 | ||
| 27 | 26 | from google.auth import _helpers | |
| 28 | 27 | from google.auth import credentials | |
@@ -128,7 +127,7 @@ def refresh(self, request): | |||
| 128 | 127 | scopes = self._scopes if self._scopes is not None else self._default_scopes | |
| 129 | 128 | # pylint: disable=unused-argument | |
| 130 | 129 | token, ttl = app_identity.get_access_token(scopes, self._service_account_id) | |
| 131 | - expiry = datetime.datetime.utcfromtimestamp(ttl) | ||
| 130 | + expiry = _helpers.utcfromtimestamp(ttl) | ||
| 132 | 131 | ||
| 133 | 132 | self.token, self.expiry = token, expiry | |
| 134 | 133 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -498,7 +498,7 @@ def _call_metadata_identity_endpoint(self, request): | |||
| 498 | 498 | raise new_exc from caught_exc | |
| 499 | 499 | ||
| 500 | 500 | _, payload, _, _ = jwt._unverified_decode(id_token) | |
| 501 | - return id_token, datetime.datetime.utcfromtimestamp(payload["exp"]) | ||
| 501 | + return id_token, _helpers.utcfromtimestamp(payload["exp"]) | ||
| 502 | 502 | ||
| 503 | 503 | def refresh(self, request): | |
| 504 | 504 | """Refreshes the ID token. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -649,7 +649,7 @@ def refresh(self, request): | |||
| 649 | 649 | raise new_exc from caught_exc | |
| 650 | 650 | ||
| 651 | 651 | self.token = id_token | |
| 652 | - self.expiry = datetime.utcfromtimestamp( | ||
| 652 | + self.expiry = _helpers.utcfromtimestamp( | ||
| 653 | 653 | jwt.decode(id_token, verify=False)["exp"] | |
| 654 | 654 | ) | |
| 655 | 655 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -368,7 +368,7 @@ def call_iam_generate_id_token_endpoint( | |||
| 368 | 368 | raise new_exc from caught_exc | |
| 369 | 369 | ||
| 370 | 370 | payload = jwt.decode(id_token, verify=False) | |
| 371 | - expiry = datetime.datetime.utcfromtimestamp(payload["exp"]) | ||
| 371 | + expiry = _helpers.utcfromtimestamp(payload["exp"]) | ||
| 372 | 372 | ||
| 373 | 373 | return id_token, expiry | |
| 374 | 374 | ||
@@ -420,7 +420,7 @@ def id_token_jwt_grant(request, token_uri, assertion, can_retry=True): | |||
| 420 | 420 | raise new_exc from caught_exc | |
| 421 | 421 | ||
| 422 | 422 | payload = jwt.decode(id_token, verify=False) | |
| 423 | - expiry = datetime.datetime.utcfromtimestamp(payload["exp"]) | ||
| 423 | + expiry = _helpers.utcfromtimestamp(payload["exp"]) | ||
| 424 | 424 | ||
| 425 | 425 | return id_token, expiry, response_data | |
| 426 | 426 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,12 +23,12 @@ | |||
| 23 | 23 | .. _Section 3.1 of rfc6749: https://tools.ietf.org/html/rfc6749#section-3.2 | |
| 24 | 24 | """ | |
| 25 | 25 | ||
| 26 | - import datetime | ||
| 27 | 26 | import http.client as http_client | |
| 28 | 27 | import json | |
| 29 | 28 | import urllib | |
| 30 | 29 | ||
| 31 | 30 | from google.auth import _exponential_backoff | |
| 31 | + from google.auth import _helpers | ||
| 32 | 32 | from google.auth import exceptions | |
| 33 | 33 | from google.auth import jwt | |
| 34 | 34 | from google.oauth2 import _client as client | |
@@ -227,7 +227,7 @@ async def id_token_jwt_grant(request, token_uri, assertion, can_retry=True): | |||
| 227 | 227 | raise new_exc from caught_exc | |
| 228 | 228 | ||
| 229 | 229 | payload = jwt.decode(id_token, verify=False) | |
| 230 | - expiry = datetime.datetime.utcfromtimestamp(payload["exp"]) | ||
| 230 | + expiry = _helpers.utcfromtimestamp(payload["exp"]) | ||
| 231 | 231 | ||
| 232 | 232 | return id_token, expiry, response_data | |
| 233 | 233 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -758,7 +758,7 @@ def test_default_state(self, get): | |||
| 758 | 758 | ||
| 759 | 759 | @mock.patch( | |
| 760 | 760 | "google.auth._helpers.utcnow", | |
| 761 | - return_value=datetime.datetime.utcfromtimestamp(0), | ||
| 761 | + return_value=_helpers.utcfromtimestamp(0), | ||
| 762 | 762 | ) | |
| 763 | 763 | @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) | |
| 764 | 764 | @mock.patch("google.auth.iam.Signer.sign", autospec=True) | |
@@ -791,7 +791,7 @@ def test_make_authorization_grant_assertion(self, sign, get, utcnow): | |||
| 791 | 791 | ||
| 792 | 792 | @mock.patch( | |
| 793 | 793 | "google.auth._helpers.utcnow", | |
| 794 | - return_value=datetime.datetime.utcfromtimestamp(0), | ||
| 794 | + return_value=_helpers.utcfromtimestamp(0), | ||
| 795 | 795 | ) | |
| 796 | 796 | @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) | |
| 797 | 797 | @mock.patch("google.auth.iam.Signer.sign", autospec=True) | |
@@ -823,7 +823,7 @@ def test_with_service_account(self, sign, get, utcnow): | |||
| 823 | 823 | ||
| 824 | 824 | @mock.patch( | |
| 825 | 825 | "google.auth._helpers.utcnow", | |
| 826 | - return_value=datetime.datetime.utcfromtimestamp(0), | ||
| 826 | + return_value=_helpers.utcfromtimestamp(0), | ||
| 827 | 827 | ) | |
| 828 | 828 | @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) | |
| 829 | 829 | @mock.patch("google.auth.iam.Signer.sign", autospec=True) | |
@@ -879,7 +879,7 @@ def test_token_uri(self): | |||
| 879 | 879 | ||
| 880 | 880 | @mock.patch( | |
| 881 | 881 | "google.auth._helpers.utcnow", | |
| 882 | - return_value=datetime.datetime.utcfromtimestamp(0), | ||
| 882 | + return_value=_helpers.utcfromtimestamp(0), | ||
| 883 | 883 | ) | |
| 884 | 884 | @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) | |
| 885 | 885 | @mock.patch("google.auth.iam.Signer.sign", autospec=True) | |
@@ -1001,7 +1001,7 @@ def test_with_target_audience_integration(self): | |||
| 1001 | 1001 | ||
| 1002 | 1002 | @mock.patch( | |
| 1003 | 1003 | "google.auth._helpers.utcnow", | |
| 1004 | - return_value=datetime.datetime.utcfromtimestamp(0), | ||
| 1004 | + return_value=_helpers.utcfromtimestamp(0), | ||
| 1005 | 1005 | ) | |
| 1006 | 1006 | @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) | |
| 1007 | 1007 | @mock.patch("google.auth.iam.Signer.sign", autospec=True) | |
@@ -1040,7 +1040,7 @@ def test_with_quota_project(self, sign, get, utcnow): | |||
| 1040 | 1040 | ||
| 1041 | 1041 | @mock.patch( | |
| 1042 | 1042 | "google.auth._helpers.utcnow", | |
| 1043 | - return_value=datetime.datetime.utcfromtimestamp(0), | ||
| 1043 | + return_value=_helpers.utcfromtimestamp(0), | ||
| 1044 | 1044 | ) | |
| 1045 | 1045 | @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) | |
| 1046 | 1046 | @mock.patch("google.auth.iam.Signer.sign", autospec=True) | |
@@ -1062,7 +1062,7 @@ def test_with_token_uri(self, sign, get, utcnow): | |||
| 1062 | 1062 | ||
| 1063 | 1063 | @mock.patch( | |
| 1064 | 1064 | "google.auth._helpers.utcnow", | |
| 1065 | - return_value=datetime.datetime.utcfromtimestamp(0), | ||
| 1065 | + return_value=_helpers.utcfromtimestamp(0), | ||
| 1066 | 1066 | ) | |
| 1067 | 1067 | @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) | |
| 1068 | 1068 | @mock.patch("google.auth.iam.Signer.sign", autospec=True) | |
@@ -1170,7 +1170,7 @@ def test_with_quota_project_integration(self): | |||
| 1170 | 1170 | ||
| 1171 | 1171 | @mock.patch( | |
| 1172 | 1172 | "google.auth._helpers.utcnow", | |
| 1173 | - return_value=datetime.datetime.utcfromtimestamp(0), | ||
| 1173 | + return_value=_helpers.utcfromtimestamp(0), | ||
| 1174 | 1174 | ) | |
| 1175 | 1175 | @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) | |
| 1176 | 1176 | @mock.patch("google.auth.iam.Signer.sign", autospec=True) | |
@@ -1181,7 +1181,11 @@ def test_refresh_success(self, id_token_jwt_grant, sign, get, utcnow): | |||
| 1181 | 1181 | ] | |
| 1182 | 1182 | sign.side_effect = [b"signature"] | |
| 1183 | 1183 | id_token_jwt_grant.side_effect = [ | |
| 1184 | - ("idtoken", datetime.datetime.utcfromtimestamp(3600), {}) | ||
| 1184 | + ( | ||
| 1185 | + "idtoken", | ||
| 1186 | + _helpers.utcfromtimestamp(3600), | ||
| 1187 | + {}, | ||
| 1188 | + ) | ||
| 1185 | 1189 | ] | |
| 1186 | 1190 | ||
| 1187 | 1191 | request = mock.create_autospec(transport.Request, instance=True) | |
@@ -1194,7 +1198,7 @@ def test_refresh_success(self, id_token_jwt_grant, sign, get, utcnow): | |||
| 1194 | 1198 | ||
| 1195 | 1199 | # Check that the credentials have the token and proper expiration | |
| 1196 | 1200 | assert self.credentials.token == "idtoken" | |
| 1197 | - assert self.credentials.expiry == (datetime.datetime.utcfromtimestamp(3600)) | ||
| 1201 | + assert self.credentials.expiry == _helpers.utcfromtimestamp(3600) | ||
| 1198 | 1202 | ||
| 1199 | 1203 | # Check the credential info | |
| 1200 | 1204 | assert self.credentials.service_account_email == "service-account@example.com" | |
@@ -1205,7 +1209,7 @@ def test_refresh_success(self, id_token_jwt_grant, sign, get, utcnow): | |||
| 1205 | 1209 | ||
| 1206 | 1210 | @mock.patch( | |
| 1207 | 1211 | "google.auth._helpers.utcnow", | |
| 1208 | - return_value=datetime.datetime.utcfromtimestamp(0), | ||
| 1212 | + return_value=_helpers.utcfromtimestamp(0), | ||
| 1209 | 1213 | ) | |
| 1210 | 1214 | @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) | |
| 1211 | 1215 | @mock.patch("google.auth.iam.Signer.sign", autospec=True) | |
@@ -1232,7 +1236,7 @@ def test_refresh_error(self, sign, get, utcnow): | |||
| 1232 | 1236 | ||
| 1233 | 1237 | @mock.patch( | |
| 1234 | 1238 | "google.auth._helpers.utcnow", | |
| 1235 | - return_value=datetime.datetime.utcfromtimestamp(0), | ||
| 1239 | + return_value=_helpers.utcfromtimestamp(0), | ||
| 1236 | 1240 | ) | |
| 1237 | 1241 | @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) | |
| 1238 | 1242 | @mock.patch("google.auth.iam.Signer.sign", autospec=True) | |
@@ -1243,7 +1247,11 @@ def test_before_request_refreshes(self, id_token_jwt_grant, sign, get, utcnow): | |||
| 1243 | 1247 | ] | |
| 1244 | 1248 | sign.side_effect = [b"signature"] | |
| 1245 | 1249 | id_token_jwt_grant.side_effect = [ | |
| 1246 | - ("idtoken", datetime.datetime.utcfromtimestamp(3600), {}) | ||
| 1250 | + ( | ||
| 1251 | + "idtoken", | ||
| 1252 | + _helpers.utcfromtimestamp(3600), | ||
| 1253 | + {}, | ||
| 1254 | + ) | ||
| 1247 | 1255 | ] | |
| 1248 | 1256 | ||
| 1249 | 1257 | request = mock.create_autospec(transport.Request, instance=True) | |
@@ -1312,7 +1320,7 @@ def test_get_id_token_from_metadata( | |||
| 1312 | 1320 | } | |
| 1313 | 1321 | ||
| 1314 | 1322 | assert cred.token == SAMPLE_ID_TOKEN | |
| 1315 | - assert cred.expiry == datetime.datetime.utcfromtimestamp(SAMPLE_ID_TOKEN_EXP) | ||
| 1323 | + assert cred.expiry == _helpers.utcfromtimestamp(SAMPLE_ID_TOKEN_EXP) | ||
| 1316 | 1324 | assert cred._use_metadata_identity_endpoint | |
| 1317 | 1325 | assert cred._signer is None | |
| 1318 | 1326 | assert cred._token_uri is None | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -677,3 +677,50 @@ def test_parse_response_no_json_method(): | |||
| 677 | 677 | ||
| 678 | 678 | def test_parse_response_none(): | |
| 679 | 679 | assert _helpers._parse_response(None) is None | |
| 680 | + | ||
| 681 | + | ||
| 682 | + class TestUtcFromTimestamp: | ||
| 683 | + """Tests for the utcfromtimestamp utility function.""" | ||
| 684 | + | ||
| 685 | + @pytest.mark.parametrize( | ||
| 686 | + "ts, expected", | ||
| 687 | + [ | ||
| 688 | + (1704067200.0, datetime.datetime(2024, 1, 1, 0, 0, 0)), | ||
| 689 | + (0.0, datetime.datetime(1970, 1, 1, 0, 0, 0)), | ||
| 690 | + (1704067200.500123, datetime.datetime(2024, 1, 1, 0, 0, 0, 500123)), | ||
| 691 | + (-31536000.0, datetime.datetime(1969, 1, 1, 0, 0, 0)), | ||
| 692 | + (1000000000.0, datetime.datetime(2001, 9, 9, 1, 46, 40)), | ||
| 693 | + ], | ||
| 694 | + ids=[ | ||
| 695 | + "standard_timestamp", | ||
| 696 | + "unix_epoch", | ||
| 697 | + "subsecond_precision", | ||
| 698 | + "negative_timestamp", | ||
| 699 | + "timezone_independence", | ||
| 700 | + ], | ||
| 701 | + ) | ||
| 702 | + def test_success_cases(self, ts, expected): | ||
| 703 | + """Verify correct UTC conversion and that the result is offset-naive.""" | ||
| 704 | + result = _helpers.utcfromtimestamp(ts) | ||
| 705 | + | ||
| 706 | + # 1. Check the datetime value is correct | ||
| 707 | + assert result == expected | ||
| 708 | + | ||
| 709 | + # 2. Check it is naive (tzinfo is None) for backward compatibility | ||
| 710 | + assert result.tzinfo is None | ||
| 711 | + | ||
| 712 | + @pytest.mark.parametrize( | ||
| 713 | + "invalid_input", | ||
| 714 | + ["string", None, [123]], | ||
| 715 | + ids=["type_string", "type_none", "type_list"], | ||
| 716 | + ) | ||
| 717 | + def test_invalid_types(self, invalid_input): | ||
| 718 | + """Verify that passing invalid types raises a TypeError.""" | ||
| 719 | + with pytest.raises(TypeError): | ||
| 720 | + _helpers.utcfromtimestamp(invalid_input) | ||
| 721 | + | ||
| 722 | + def test_out_of_range(self): | ||
| 723 | + """Test very large timestamps that exceed platform limits.""" | ||
| 724 | + with pytest.raises((OverflowError, OSError, ValueError)): | ||
| 725 | + # Large enough to fail on most systems (Year 300,000+) | ||
| 726 | + _helpers.utcfromtimestamp(9999999999999) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1047,7 +1047,8 @@ def test_id_token_success( | |||
| 1047 | 1047 | id_creds.refresh(request) | |
| 1048 | 1048 | ||
| 1049 | 1049 | assert id_creds.token == ID_TOKEN_DATA | |
| 1050 | - assert id_creds.expiry == datetime.datetime.utcfromtimestamp(ID_TOKEN_EXPIRY) | ||
| 1050 | + expected_expiry = _helpers.utcfromtimestamp(ID_TOKEN_EXPIRY) | ||
| 1051 | + assert id_creds.expiry == expected_expiry | ||
| 1051 | 1052 | ||
| 1052 | 1053 | def test_id_token_metrics(self, mock_donor_credentials): | |
| 1053 | 1054 | credentials = self.make_credentials(lifetime=None) | |
@@ -1071,9 +1072,8 @@ def test_id_token_metrics(self, mock_donor_credentials): | |||
| 1071 | 1072 | id_creds.refresh(None) | |
| 1072 | 1073 | ||
| 1073 | 1074 | assert id_creds.token == ID_TOKEN_DATA | |
| 1074 | - assert id_creds.expiry == datetime.datetime.utcfromtimestamp( | ||
| 1075 | - ID_TOKEN_EXPIRY | ||
| 1076 | - ) | ||
| 1075 | + expected_expiry = _helpers.utcfromtimestamp(ID_TOKEN_EXPIRY) | ||
| 1076 | + assert id_creds.expiry == expected_expiry | ||
| 1077 | 1077 | assert ( | |
| 1078 | 1078 | mock_post.call_args.kwargs["headers"]["x-goog-api-client"] | |
| 1079 | 1079 | == ID_TOKEN_REQUEST_METRICS_HEADER_VALUE | |
@@ -1181,7 +1181,8 @@ def test_id_token_with_target_audience( | |||
| 1181 | 1181 | id_creds.refresh(request) | |
| 1182 | 1182 | ||
| 1183 | 1183 | assert id_creds.token == ID_TOKEN_DATA | |
| 1184 | - assert id_creds.expiry == datetime.datetime.utcfromtimestamp(ID_TOKEN_EXPIRY) | ||
| 1184 | + expected_expiry = _helpers.utcfromtimestamp(ID_TOKEN_EXPIRY) | ||
| 1185 | + assert id_creds.expiry == expected_expiry | ||
| 1185 | 1186 | assert id_creds._include_email is True | |
| 1186 | 1187 | ||
| 1187 | 1188 | def test_id_token_invalid_cred( | |
| Back | FazBrowse Home | New Git URL |
0 commit comments