Skip to content

Commit

Permalink
fixes #15 - no issuer_id from settings
Browse files Browse the repository at this point in the history
  • Loading branch information
jensens committed Jan 22, 2025
1 parent 8a332f6 commit 4762a21
Show file tree
Hide file tree
Showing 11 changed files with 48 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:
if: steps.changed-models-or-api.outputs.any_changed == 'true'
run: |
echo "${{ secrets.SERVICE_ACCOUNT_JSON }}" > /tmp/edutap-wallet-google-credentials.json
echo "EDUTAP_WALLET_GOOGLE_ISSUER_ID=${{ secrets.ISSUER_ID }}" > .env
echo "EDUTAP_WALLET_GOOGLE_TEST_ISSUER_ID=${{ secrets.ISSUER_ID }}" > .env
echo "EDUTAP_WALLET_GOOGLE_CREDENTIALS_FILE=/tmp/edutap-wallet-google-credentials.json" >> .env
echo "EDUTAP_WALLET_GOOGLE_INTEGRATION_TEST_PREFIX=${{ github.run_id }}" >> .env
- name: Test with tox
Expand Down
8 changes: 2 additions & 6 deletions src/edutap/wallet_google/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,13 +290,9 @@ def listing(
raise ValueError("resource_id of a class must be given to list its objects")
params["classId"] = resource_id
elif name.endswith("Class"):
is_pageable = True
if not issuer_id:
issuer_id = session_manager.settings.issuer_id
if not issuer_id:
raise ValueError(
"'issuer_id' must be passed as keyword argument or set in environment"
)
raise ValueError("issuer_id must be given to list classes")
is_pageable = True
params["issuerId"] = issuer_id

if is_pageable:
Expand Down
1 change: 0 additions & 1 deletion src/edutap/wallet_google/handlers/fastapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ async def handle_image(request: Request, encrypted_image_id: str):
# needs to be included after the routers are defined
router = APIRouter(
prefix=session_manager.settings.handler_prefix,
tags=["edutap", "google_wallet"],
)
router.include_router(router_callback)
router.include_router(router_images)
15 changes: 11 additions & 4 deletions src/edutap/wallet_google/handlers/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,18 @@ def verified_signed_message(data: CallbackData) -> SignedMessage:
Verifies the signature of the callback data.
and returns the parsed SignedMessage
"""
# parse message
message = SignedMessage.model_validate_json(data.signedMessage)

# get issuer_id
if not message.classId or "." not in message.classId:
raise ValueError("Missing classId")
issuer_id = message.classId.split(".")[0]

# shortcut if signature validation is disabled
settings = session_manager.settings
if settings.handler_callback_verify_signature == "0":
# shortcut if signature validation is disabled
return SignedMessage.model_validate_json(data.signedMessage)
return message

if data.protocolVersion != PROTOCOL_VERSION:
raise ValueError("Invalid protocolVersion")
Expand All @@ -166,7 +173,7 @@ def verified_signed_message(data: CallbackData) -> SignedMessage:
signature = base64.decodebytes(bytes(data.signature, "utf-8"))
signed_data = _construct_signed_data(
"GooglePayWallet",
settings.issuer_id,
issuer_id,
PROTOCOL_VERSION,
data.signedMessage,
)
Expand All @@ -175,4 +182,4 @@ def verified_signed_message(data: CallbackData) -> SignedMessage:
except (ValueError, InvalidSignature):
raise ValueError("Invalid signature")

return SignedMessage.model_validate_json(data.signedMessage)
return message
4 changes: 1 addition & 3 deletions src/edutap/wallet_google/settings.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from pathlib import Path
from pydantic import EmailStr
from pydantic import Field
from pydantic import HttpUrl
from pydantic_settings import BaseSettings
Expand Down Expand Up @@ -47,8 +46,7 @@ class Settings(BaseSettings):

credentials_file: Path = ROOT_DIR / "tests" / "data" / "credentials_fake.json"
credentials_scopes: list[str] = SCOPES
issuer_account_email: EmailStr | None = None
issuer_id: str = Field(default="")
test_issuer_id: str = Field(default="")

fernet_encryption_key: str = ""

Expand Down
7 changes: 7 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,10 @@ def mock_settings():
@pytest.fixture
def mock_fernet_encryption_key(mock_settings):
mock_settings.fernet_encryption_key = "TDTPJVv24gha-jRX0apPgPpMDN2wX1kVSNNZdWXcz8E="


@pytest.fixture
def test_issuer_id():
from edutap.wallet_google.session import session_manager

yield session_manager.settings.test_issuer_id
8 changes: 5 additions & 3 deletions tests/data/test_wallet_google_plugins/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ async def image_by_id(self, image_id: str) -> ImageData:
class TestCallbackHandler:
"""
Implementation of edutap.wallet_google.protocols.CallbackHandler
Used in tests to simulate a callback handler and possible errors.
"""

async def handle(
Expand All @@ -35,8 +37,8 @@ async def handle(
count: int,
nonce: str,
) -> None:
if class_id == "TIMEOUT":
if class_id.startswith("TIMEOUT"):
await asyncio.sleep(exp_time_millis / 1000)
elif class_id:
elif nonce:
return
raise ValueError("class_id is required")
raise ValueError("test case errors if nonce is 0")
6 changes: 3 additions & 3 deletions tests/integration/test_CRULM.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def test_class_object_cru(type_base, class_data, object_data, integration_test_i
############################
# test class
class_base = f"{integration_test_id}.{class_type}.test_CRU.wallet_google.edutap"
class_data["id"] = f"{session_manager.settings.issuer_id}.{class_base}"
class_data["id"] = f"{session_manager.settings.test_issuer_id}.{class_base}"

data = new(class_type, class_data)

Expand Down Expand Up @@ -186,14 +186,14 @@ def test_class_object_cru(type_base, class_data, object_data, integration_test_i
assert result_message.id == class_data["id"]

# list all
result_list = [x for x in listing(name=class_type)]
result_list = [x for x in listing(name=class_type, issuer_id=session_manager.settings.test_issuer_id)]
assert len(result_list) > 0

############################
# test object
object_data["classId"] = class_data["id"]
object_base = f"{integration_test_id}.{object_type}.test_CRU.wallet_google.edutap"
object_data["id"] = f"{session_manager.settings.issuer_id}.{object_base}"
object_data["id"] = f"{session_manager.settings.test_issuer_id}.{object_base}"
odata = new(object_type, object_data)

# create
Expand Down
21 changes: 13 additions & 8 deletions tests/test_api_save_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_create_claims():
),
]
expected = {
"iss": "123456789",
"iss": "[email protected]",
"aud": "google",
"typ": "savetowallet",
"iat": "1737115974",
Expand All @@ -55,7 +55,7 @@ def test_create_claims():
}

claims = api._create_claims(
"123456789",
"[email protected]",
[],
models,
iat=datetime.datetime(2025, 1, 17, 12, 12, 54, 0, datetime.timezone.utc),
Expand All @@ -74,7 +74,6 @@ def test_create_claims():
def test_api_save_link(mock_settings):
from edutap.wallet_google.settings import ROOT_DIR

mock_settings.issuer_id = "1234567890123456789"
mock_settings.credentials_file = (
ROOT_DIR / "tests" / "data" / "credentials_fake.json"
)
Expand All @@ -84,17 +83,23 @@ def test_api_save_link(mock_settings):
link = api.save_link(
[
api.new(
"Reference", {"id": "test-1.edutap.eu", "model_name": "GenericObject"}
"Reference",
{
"id": "1234567890123456789.test-1.edutap.eu",
"model_name": "GenericObject",
},
),
api.new(
"OfferObject",
{"id": "test-2.edutap.eu", "classId": "test-class-1.edutap.eu"},
{
"id": "1234567890123456789.test-2.edutap.eu",
"classId": "1234567890123456789.test-class-1.edutap.eu",
},
),
],
iat=datetime.datetime(2025, 1, 22, 10, 20, 0, 0, datetime.timezone.utc)

iat=datetime.datetime(2025, 1, 22, 10, 20, 0, 0, datetime.timezone.utc),
)
expected = "https://pay.google.com/gp/v/save/eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJSUzI1NiIsICJraWQiOiAiMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3OCJ9.eyJpc3MiOiAiZWR1dGFwLXRlc3QtZXhhbXBsZUBzb2RpdW0tcmF5LTEyMzQ1Ni5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsICJhdWQiOiAiZ29vZ2xlIiwgInR5cCI6ICJzYXZldG93YWxsZXQiLCAiaWF0IjogIjE3Mzc1NDEyMDAiLCAiZXhwIjogIiIsICJwYXlsb2FkIjogeyJvZmZlck9iamVjdHMiOiBbeyJpZCI6ICJ0ZXN0LTIuZWR1dGFwLmV1IiwgImNsYXNzSWQiOiAidGVzdC1jbGFzcy0xLmVkdXRhcC5ldSIsICJzdGF0ZSI6ICJTVEFURV9VTlNQRUNJRklFRCIsICJoYXNMaW5rZWREZXZpY2UiOiBmYWxzZSwgImRpc2FibGVFeHBpcmF0aW9uTm90aWZpY2F0aW9uIjogZmFsc2UsICJub3RpZnlQcmVmZXJlbmNlIjogIk5PVElGSUNBVElPTl9TRVRUSU5HU19GT1JfVVBEQVRFU19VTlNQRUNJRklFRCJ9XSwgImdlbmVyaWNPYmplY3RzIjogW3siaWQiOiAidGVzdC0xLmVkdXRhcC5ldSJ9XX0sICJvcmlnaW5zIjogW119.u8xDMKKdPBB0yjYqR-uM4eAYMEskRZyv_AOBhGkZ0oswvr-nVOs4jogXZo6cOmSvzjE_tRviNf_GHDelOaND-c4AqNwTg13DRG0c-aNWKbROTlrZefG0dusPcAuhTwzG-gsDn_sCstHWy8gkKQOmb_x4RjRB-b_gsv2uhmeKtNPvofxBNLUHbOefYKL12PPII9kI00Dl0pAyh0dgqI3yew0197a2rYl6_lOlYfO4jd784b-3CDCDKpOZnEjqBBedbLSDhKdWV10eo9mz6OsgqydERuUDDzhJopkwz6BIFL_HA_IHeAaiLtoSNbuOqc7zUecgOHlqecaWBZhV_-WPkQ"
expected = "https://pay.google.com/gp/v/save/eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJSUzI1NiIsICJraWQiOiAiMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3OCJ9.eyJpc3MiOiAiZWR1dGFwLXRlc3QtZXhhbXBsZUBzb2RpdW0tcmF5LTEyMzQ1Ni5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsICJhdWQiOiAiZ29vZ2xlIiwgInR5cCI6ICJzYXZldG93YWxsZXQiLCAiaWF0IjogIjE3Mzc1NDEyMDAiLCAiZXhwIjogIiIsICJwYXlsb2FkIjogeyJvZmZlck9iamVjdHMiOiBbeyJpZCI6ICIxMjM0NTY3ODkwMTIzNDU2Nzg5LnRlc3QtMi5lZHV0YXAuZXUiLCAiY2xhc3NJZCI6ICIxMjM0NTY3ODkwMTIzNDU2Nzg5LnRlc3QtY2xhc3MtMS5lZHV0YXAuZXUiLCAic3RhdGUiOiAiU1RBVEVfVU5TUEVDSUZJRUQiLCAiaGFzTGlua2VkRGV2aWNlIjogZmFsc2UsICJkaXNhYmxlRXhwaXJhdGlvbk5vdGlmaWNhdGlvbiI6IGZhbHNlLCAibm90aWZ5UHJlZmVyZW5jZSI6ICJOT1RJRklDQVRJT05fU0VUVElOR1NfRk9SX1VQREFURVNfVU5TUEVDSUZJRUQifV0sICJnZW5lcmljT2JqZWN0cyI6IFt7ImlkIjogIjEyMzQ1Njc4OTAxMjM0NTY3ODkudGVzdC0xLmVkdXRhcC5ldSJ9XX0sICJvcmlnaW5zIjogW119.OJcaFwQ9XlKvMb0t-aokejx39zeHiPMIkA5EQSU7oPXgFy740OHFN0-vsw-8ABMc4HB-ZipuH6ZN1MhbOc1baabPl4s8XLU_Z43NMUXmKKz2KExt2F7qiuy_04aeFZQVBGoZOCQicjD_qrh6YqGYHdQFkcjtOAAdy0JPwVsfdiowTdDv4EdH2eZXLvE3bKYeu1rvD6oBS5xCIDwu77t3DurrKYf1yYfOL4YpnNAwru-o7MM_J-FKzVur6wJ0xQgYCP_UtX8PxHoj2-YXQ0yXlypgw8Vpo069hcrOVyQaRdrFEVf_TKi6IBLpPr6zxk19NhmJ9qjwIUygMEkyfRf3Mw"
assert link == expected


Expand Down
6 changes: 3 additions & 3 deletions tests/test_handler_fastapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def test_callback_disabled_signature_check_ERROR(mock_settings):
mock_settings.handler_callback_verify_signature = "0"

callback_data = CallbackData(**real_callback_data)
callback_data.signedMessage = '{"classId":"","objectId":"","eventType":"save","expTimeMillis":1734366082269,"count":1,"nonce":""}'
callback_data.signedMessage = '{"classId":"1.x","objectId":"1.y","eventType":"save","expTimeMillis":1734366082269,"count":1,"nonce":""}'

from edutap.wallet_google.handlers.fastapi import router

Expand Down Expand Up @@ -73,7 +73,7 @@ def raise_not_implemented():
)

callback_data = CallbackData(**real_callback_data)
callback_data.signedMessage = '{"classId":"","objectId":"","eventType":"save","expTimeMillis":1734366082269,"count":1,"nonce":""}'
callback_data.signedMessage = '{"classId":"1.x","objectId":"1.y","eventType":"save","expTimeMillis":1734366082269,"count":1,"nonce":"abcde"}'

from edutap.wallet_google.handlers.fastapi import router

Expand All @@ -96,7 +96,7 @@ def test_callback_disabled_signature_check_TIMEOUT(mock_settings):
mock_settings.handlers_callback_timeout = 0.1

callback_data = CallbackData(**real_callback_data)
callback_data.signedMessage = '{"classId":"TIMEOUT","objectId":"","eventType":"save","expTimeMillis":250,"count":1,"nonce":""}'
callback_data.signedMessage = '{"classId":"TIMEOUT.x","objectId":"1.x","eventType":"save","expTimeMillis":250,"count":1,"nonce":"abcde"}'

from edutap.wallet_google.handlers.fastapi import router

Expand Down
4 changes: 2 additions & 2 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def test_base_settings():

def test_local_settings(monkeypatch):
monkeypatch.setenv(
"EDUTAP_WALLET_GOOGLE_ISSUER_ID",
"EDUTAP_WALLET_GOOGLE_TEST_ISSUER_ID",
"1234567890123456789",
)
monkeypatch.setenv(
Expand All @@ -29,7 +29,7 @@ def test_local_settings(monkeypatch):

settings = Settings()

assert settings.issuer_id == "1234567890123456789"
assert settings.test_issuer_id == "1234567890123456789"
assert settings.credentials_file.exists()


Expand Down

0 comments on commit 4762a21

Please sign in to comment.