Skip to content

Commit

Permalink
validation stub test and settings refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
jensens committed Dec 6, 2024
1 parent db67b70 commit 99ae672
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
This module was vendored from https://github.com/yoyowallet/google-pay-token-decryption
Copyright is by it's original authors at Yoyo Wallet <dev@yoyowallet.com>
Copyright is by its original authors at Yoyo Wallet <dev@yoyowallet.com>
It is under the MIT License, as found here https://github.com/yoyowallet/google-pay-token-decryption/blob/5cd006da9687171c1e35b55507b671c6e4eb513d/pyproject.toml#L8
"""

Expand Down
2 changes: 1 addition & 1 deletion src/edutap/wallet_google/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ def save_link(

claims = {
"iat": "",
"iss": session_manager.credentials_info["client_email"],
"iss": session_manager.settings.credentials_info["client_email"],
"aud": "google",
"origins": origins,
"typ": "savetowallet",
Expand Down
12 changes: 8 additions & 4 deletions src/edutap/wallet_google/handlers/validate.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .._vendor.google_pay_token_decryption import GooglePayError
from .._vendor.google_pay_token_decryption import GooglePayTokenDecryptor
from ..models.callback import CallbackData
from ..settings import GoogleWalletSettings
Expand All @@ -10,9 +11,12 @@ def verify_signature(data: CallbackData) -> bool:
# TODO
settings = GoogleWalletSettings()
decryptor = GooglePayTokenDecryptor(
settings.credentials_file,
settings.issuer_account_email,
settings.issuer_id,
settings.google_root_signing_public_keys.dict()["keys"],
settings.credentials_info["issuer_id"],
settings.credentials_info["private_key"],
)
decryptor.decrypt(data)
try:
decryptor.verify_signature(dict(data))
except GooglePayError:
return False
return True
14 changes: 0 additions & 14 deletions src/edutap/wallet_google/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,6 @@ class SessionManager:
def __init__(self):
self.settings = GoogleWalletSettings()

@property
def credentials_info(self) -> dict[str, str]:
if not self.settings.credentials_file.exists:
raise ValueError(
f"EDUTAP_WALLET_GOOGLE_CREDENTIALS_FILE={self.settings.credentials_file} does not exist."
)
with open(self.settings.credentials_file) as fp:
self._credentials_info = json.load(fp)
if not isinstance(self._credentials_info, dict):
raise ValueError(
f"EDUTAP_WALLET_GOOGLE_CREDENTIALS_FILE={self.settings.credentials_file} content is not a dict"
)
return self._credentials_info

def _make_session(self) -> AuthorizedSession:
if not self.settings.credentials_file.exists:
raise ValueError(
Expand Down
53 changes: 32 additions & 21 deletions src/edutap/wallet_google/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
from pydantic import HttpUrl
from pydantic_settings import BaseSettings
from pydantic_settings import SettingsConfigDict
from typing import Any
from typing import Literal

import json
import requests


Expand All @@ -17,15 +19,10 @@
SCOPE = "https://www.googleapis.com/auth/wallet_object.issuer"
GOOGLE_ROOT_SIGNING_PUBLIC_KEYS_URL = {
# see https://developers.google.com/pay/api/android/guides/resources/payment-data-cryptography#root-signing-keys
"testing": {
"url": "https://payments.developers.google.com/paymentmethodtoken/test/keys.json",
"value": None,
},
"production": {
"url": "https://payments.developers.google.com/paymentmethodtoken/keys.json",
"value": None,
},
"testing": "https://payments.developers.google.com/paymentmethodtoken/test/keys.json",
"production": "https://payments.developers.google.com/paymentmethodtoken/keys.json",
}
GOOGLE_ROOT_SIGNING_PUBLIC_KEYS_VALUE: dict[str, RootSigningPublicKeys] = {}


class GoogleWalletSettings(BaseSettings):
Expand Down Expand Up @@ -59,19 +56,33 @@ class GoogleWalletSettings(BaseSettings):

environment: Literal["production", "testing"] = "testing"

google_root_signing_public_keys: RootSigningPublicKeys | None = None
@property
def google_root_signing_public_keys(self) -> RootSigningPublicKeys:
"""
Fetch Googles root signing keys once for the configured environment and return them or the cached value.
"""
if (
GOOGLE_ROOT_SIGNING_PUBLIC_KEYS_VALUE.get(self.environment, None)
is not None
):
return GOOGLE_ROOT_SIGNING_PUBLIC_KEYS_VALUE[self.environment]
# fetch once
resp = requests.get(GOOGLE_ROOT_SIGNING_PUBLIC_KEYS_URL[self.environment])
resp.raise_for_status()
return RootSigningPublicKeys.model_validate_json(resp.text)

def __init__(self):
super().__init__()
if GOOGLE_ROOT_SIGNING_PUBLIC_KEYS_URL[self.environment]["value"] is None:
resp = requests.get(
GOOGLE_ROOT_SIGNING_PUBLIC_KEYS_URL[self.environment]["url"]
@property
def credentials_info(self) -> dict[str, str]:
if credentials_info := getattr(self, "_credentials_info"):
return credentials_info
if not self.credentials_file.exists():
raise ValueError(
f"EDUTAP_WALLET_GOOGLE_CREDENTIALS_FILE={self.credentials_file} does not exist."
)
resp.raise_for_status()
self.google_root_signing_public_keys = (
RootSigningPublicKeys.model_validate_json(resp.text)
with open(self.credentials_file) as fp:
self._credentials_info: dict[str, Any] = json.load(fp)
if not isinstance(self._credentials_info, dict):
raise ValueError(
f"EDUTAP_WALLET_GOOGLE_CREDENTIALS_FILE={self.credentials_file} content is not a dict"
)
else:
self.google_root_signing_public_keys = GOOGLE_ROOT_SIGNING_PUBLIC_KEYS_URL[
self.environment
]["value"]
return self._credentials_info
4 changes: 2 additions & 2 deletions tests/test_handler_validate.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from edutap.wallet_google.handlers.validate import verify_signature


def test_hndler_validate_valid():
assert verify_signature("data") == True
def test_handler_validate_valid():
assert verify_signature("data") is True

0 comments on commit 99ae672

Please sign in to comment.