diff --git a/src/edutap/wallet_google/api.py b/src/edutap/wallet_google/api.py index b939afb..2ca7739 100644 --- a/src/edutap/wallet_google/api.py +++ b/src/edutap/wallet_google/api.py @@ -339,6 +339,21 @@ def listing( break +def _convert_str_or_datetime_to_str(value: str | datetime.datetime) -> str: + """convert and check the value to be a valid string for the JWT claim timestamps""" + if isinstance(value, datetime.datetime): + return str(int(value.timestamp())) + if value == "": + return value + if not value.isdecimal(): + raise ValueError("string must be a decimal") + if int(value) < 0: + raise ValueError("string must be an int >= 0 number") + if int(value) > 2**32: + raise ValueError("string must be an int < 2**32 number") + return value + + def save_link( models: list[ClassModel | ObjectModel | Reference], *, @@ -363,6 +378,8 @@ def save_link( The Google Wallet API button will not render when the origins field is not defined. You could potentially get an "Load denied by X-Frame-Options" or "Refused to display" messages in the browser console when the origins field is not defined. + :param: iat: Issued At Time. The time when the JWT was issued. + :param: exp: Expiration Time. The time when the JWT expires. :return: Link with JWT to save the resources to the wallet. """ payload = JWTPayload() @@ -378,26 +395,12 @@ def save_link( setattr(payload, name, []) getattr(payload, name).append(model) - if isinstance(iat, datetime.datetime): - iat = str(int(iat.timestamp())) - if iat != "": - assert iat.isdecimal() - assert int(iat) >= 0 - assert int(iat) < 2**32 - - if isinstance(exp, datetime.datetime): - exp = str(int(exp.timestamp())) - if exp != "": - assert exp.isdecimal() - assert int(exp) >= 0 - assert int(exp) < 2**32 - claims = JWTClaims( iss=session_manager.settings.credentials_info["client_email"], origins=origins, payload=payload, - iat=iat, - exp=exp, + iat=_convert_str_or_datetime_to_str(iat), + exp=_convert_str_or_datetime_to_str(exp), ) logger.debug(claims.model_dump_json(indent=2)) diff --git a/tests/test_api_save_link.py b/tests/test_api_save_link.py index 253cfaa..09cdab9 100644 --- a/tests/test_api_save_link.py +++ b/tests/test_api_save_link.py @@ -1,3 +1,6 @@ +import pytest + + def test_api_save_link(mock_settings): from edutap.wallet_google.settings import ROOT_DIR @@ -19,7 +22,48 @@ def test_api_save_link(mock_settings): ), ] ) - assert ( - link - == "https://pay.google.com/gp/v/save/eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJSUzI1NiIsICJraWQiOiAiMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3OCJ9.eyJpc3MiOiAiZWR1dGFwLXRlc3QtZXhhbXBsZUBzb2RpdW0tcmF5LTEyMzQ1Ni5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsICJhdWQiOiAiZ29vZ2xlIiwgInR5cCI6ICJzYXZldHRvd2FsbGV0IiwgImlhdCI6ICIiLCAicGF5bG9hZCI6IHsib2ZmZXJPYmplY3RzIjogW3siaWQiOiAidGVzdC0yLmVkdXRhcC5ldSIsICJjbGFzc0lkIjogInRlc3QtY2xhc3MtMS5lZHV0YXAuZXUiLCAic3RhdGUiOiAiU1RBVEVfVU5TUEVDSUZJRUQiLCAiaGFzTGlua2VkRGV2aWNlIjogZmFsc2UsICJkaXNhYmxlRXhwaXJhdGlvbk5vdGlmaWNhdGlvbiI6IGZhbHNlLCAibm90aWZ5UHJlZmVyZW5jZSI6ICJOT1RJRklDQVRJT05fU0VUVElOR1NfRk9SX1VQREFURVNfVU5TUEVDSUZJRUQifV0sICJnZW5lcmljT2JqZWN0cyI6IFt7ImlkIjogInRlc3QtMS5lZHV0YXAuZXUifV19LCAib3JpZ2lucyI6IFtdfQ.LEPJBlt7ic9cPWKUvpoxUWe5yvdK0_kqPlBFkHmqFBfO5eeYN-owTHCElCGhnHeE730D4U3XjQWeZXfcaEAQcdBKB8udoT2Tja7Rw_M8M18kpBrSdGDRKT_uXG_-RkG3uVB30Lu5otlJiX2VOJWg9H6NR7wD_pfUt67cLjiBeMILuIVi-h0CDUV0dObEjnOHrRhj6KeKdfqq6izwwmw4iSQxsaQrDxWZtwCZ__pV5UK54Od6-lNrsBQwz241SDYv9kJTXrImrjRZXdoht6xgwqxg-GcuqUJgcczG-TLyN_9aI4FtA2cz8PCXyKPXnd-_HTe9nohi05dfMDeVWsmP6g" - ) + expected = "https://pay.google.com/gp/v/save/eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJSUzI1NiIsICJraWQiOiAiMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3OCJ9.eyJpc3MiOiAiZWR1dGFwLXRlc3QtZXhhbXBsZUBzb2RpdW0tcmF5LTEyMzQ1Ni5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsICJhdWQiOiAiZ29vZ2xlIiwgInR5cCI6ICJzYXZldG93YWxsZXQiLCAiaWF0IjogIiIsICJleHAiOiAiIiwgInBheWxvYWQiOiB7Im9mZmVyT2JqZWN0cyI6IFt7ImlkIjogInRlc3QtMi5lZHV0YXAuZXUiLCAiY2xhc3NJZCI6ICJ0ZXN0LWNsYXNzLTEuZWR1dGFwLmV1IiwgInN0YXRlIjogIlNUQVRFX1VOU1BFQ0lGSUVEIiwgImhhc0xpbmtlZERldmljZSI6IGZhbHNlLCAiZGlzYWJsZUV4cGlyYXRpb25Ob3RpZmljYXRpb24iOiBmYWxzZSwgIm5vdGlmeVByZWZlcmVuY2UiOiAiTk9USUZJQ0FUSU9OX1NFVFRJTkdTX0ZPUl9VUERBVEVTX1VOU1BFQ0lGSUVEIn1dLCAiZ2VuZXJpY09iamVjdHMiOiBbeyJpZCI6ICJ0ZXN0LTEuZWR1dGFwLmV1In1dfSwgIm9yaWdpbnMiOiBbXX0.MaHyvgpwdAl9XB0CS6pO2IYoORPM8XADJZama13cYCVuaPN4VOg3k8TA3jOAyj34uqjG3u6Yr2HGGaKYHh7lfKax2L5CcNEJg02zjTsi7qjGpzMZr62CKhuw2JqzQvsIj8Wug4_zE4ErGASAZdKvZr2bpJWPXmmk9JibOuFpotTwpTd6CmLMhtypp_wyHinRX7FzXTT_V4_SA3ZYEblWF7FBz_m9NsbQjXYQRE0yxCY_bQEDydqK2tzOVd3YGiqLK_zZjBA3H3_rtqY6pdPdyUlADIWTU0dJ0sc1sNl0rjGFCgeMdziNhgppjxQ0iWnVyFb2hZO8QEi83aD5A46X9A" + + assert link == expected + + +def test__convert_str_or_datetime_to_str__timestamp(): + from edutap.wallet_google.api import _convert_str_or_datetime_to_str + + import datetime + + dt = datetime.datetime(2021, 1, 1, 12, 0, 0) + expected = '1609498800' + + assert _convert_str_or_datetime_to_str(dt) == expected + + +def test__convert_str_or_datetime_to_str__str_int_lt_zero(): + from edutap.wallet_google.api import _convert_str_or_datetime_to_str + + import datetime + + + with pytest.raises(ValueError) as exc: + _convert_str_or_datetime_to_str("-1") + + +def test__convert_str_or_datetime_to_str__str_int_tr_4bytes(): + from edutap.wallet_google.api import _convert_str_or_datetime_to_str + + import datetime + + + with pytest.raises(ValueError) as exc: + _convert_str_or_datetime_to_str(f"{2**32+1}") + +def test__convert_str_or_datetime_to_str__not_decimal(): + from edutap.wallet_google.api import _convert_str_or_datetime_to_str + + import datetime + + + with pytest.raises(ValueError) as exc: + _convert_str_or_datetime_to_str("x 100") + +