Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions api/organisations/usage_reporting/mappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@
MAX_PROJECT_USAGE_ROWS = 5_000


def map_signature_to_control_plane_auth_token(signature: str) -> str:
return (
base64.urlsafe_b64encode(signature.encode("utf-8")).decode("ascii").rstrip("=")
)
def map_signature_to_control_plane_auth_token(signature_b64: str) -> str:
raw_signature = base64.b64decode(signature_b64)
return base64.urlsafe_b64encode(raw_signature).decode("ascii").rstrip("=")


def map_usage_data_to_total_api_calls(usage_data: list[UsageData]) -> int:
Expand Down
6 changes: 3 additions & 3 deletions api/organisations/usage_reporting/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ def push_snapshot(
*,
base_url: str,
snapshot: UsageSnapshot,
signature: str,
signature_b64: str,
) -> None:
url = f"{base_url.rstrip('/')}{USAGE_ENDPOINT_PATH}"
headers = {
"Authorization": (
f"Bearer {map_signature_to_control_plane_auth_token(signature)}"
f"Bearer {map_signature_to_control_plane_auth_token(signature_b64)}"
),
"Content-Type": "application/json",
}
Expand Down Expand Up @@ -69,7 +69,7 @@ def push_usage_snapshots() -> None:
push_snapshot(
base_url=base_url,
snapshot=map_organisation_to_usage_snapshot(organisation),
signature=organisation.licence.signature,
signature_b64=organisation.licence.signature,
)
except Exception:
logger.exception("snapshot.errored")
16 changes: 8 additions & 8 deletions api/tests/unit/organisations/usage_reporting/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def test_push_snapshot__status_code__logs_expected_event(
push_snapshot(
base_url="https://cp.example.com/",
snapshot=snapshot,
signature="sig",
signature_b64="c2ln",
)

# Then
Expand All @@ -105,21 +105,21 @@ def test_push_snapshot__valid_snapshot__sends_bearer_authed_post(
# Given
mocked_post = mocker.patch("organisations.usage_reporting.services.requests.post")
mocked_post.return_value.status_code = 201
signature = "abc+/=def=="
signature_b64 = "++++ABE="

# When
push_snapshot(
base_url="https://cp.example.com/",
snapshot=snapshot,
signature=signature,
signature_b64=signature_b64,
)

# Then
mocked_post.assert_called_once()
(url,), kwargs = mocked_post.call_args
assert url == "https://cp.example.com/v1/public/usage"
# The unpadded base64url token the Control Plane receives on the wire
assert kwargs["headers"]["Authorization"] == "Bearer YWJjKy89ZGVmPT0"
# The signature's raw bytes, unpadded base64url-encoded for the wire.
assert kwargs["headers"]["Authorization"] == "Bearer ----ABE"
assert kwargs["headers"]["Content-Type"] == "application/json"
assert json.loads(kwargs["data"]) == {
"timestamp": "2026-06-18T08:00:00Z",
Expand Down Expand Up @@ -200,10 +200,10 @@ def test_push_usage_snapshots__licensed_organisations__pushes_each(
# Then
assert mocked_push.call_count == 2
mocked_push.assert_any_call(
base_url="https://cp.example.com", snapshot=snapshot, signature="sig-1"
base_url="https://cp.example.com", snapshot=snapshot, signature_b64="sig-1"
)
mocked_push.assert_any_call(
base_url="https://cp.example.com", snapshot=snapshot, signature="sig-2"
base_url="https://cp.example.com", snapshot=snapshot, signature_b64="sig-2"
)


Expand Down Expand Up @@ -233,5 +233,5 @@ def test_push_usage_snapshots__one_organisation_raises__continues(
# Then - first organisation failed (logged) but second still pushed
assert log.has("snapshot.errored", level="error")
mocked_push.assert_called_once_with(
base_url="https://cp.example.com", snapshot=snapshot, signature="sig-2"
base_url="https://cp.example.com", snapshot=snapshot, signature_b64="sig-2"
)
Loading