68 lines
2.2 KiB
Python
68 lines
2.2 KiB
Python
from __future__ import annotations
|
|
|
|
import base64
|
|
|
|
import pytest
|
|
from cryptography.hazmat.primitives import serialization
|
|
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
|
|
|
|
from app.services.openclaw.device_identity import (
|
|
build_device_auth_payload,
|
|
load_or_create_device_identity,
|
|
sign_device_payload,
|
|
)
|
|
|
|
|
|
def _base64url_decode(value: str) -> bytes:
|
|
padding = "=" * ((4 - len(value) % 4) % 4)
|
|
return base64.urlsafe_b64decode(f"{value}{padding}")
|
|
|
|
|
|
def test_load_or_create_device_identity_persists_same_identity(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path,
|
|
) -> None:
|
|
identity_path = tmp_path / "identity" / "device.json"
|
|
monkeypatch.setenv("OPENCLAW_GATEWAY_DEVICE_IDENTITY_PATH", str(identity_path))
|
|
|
|
first = load_or_create_device_identity()
|
|
second = load_or_create_device_identity()
|
|
|
|
assert identity_path.exists()
|
|
assert first.device_id == second.device_id
|
|
assert first.public_key_pem.strip() == second.public_key_pem.strip()
|
|
assert first.private_key_pem.strip() == second.private_key_pem.strip()
|
|
|
|
|
|
def test_build_device_auth_payload_uses_nonce_for_v2() -> None:
|
|
payload = build_device_auth_payload(
|
|
device_id="dev",
|
|
client_id="gateway-client",
|
|
client_mode="backend",
|
|
role="operator",
|
|
scopes=["operator.read", "operator.admin"],
|
|
signed_at_ms=123,
|
|
token="token",
|
|
nonce="nonce-xyz",
|
|
)
|
|
|
|
assert payload == (
|
|
"v2|dev|gateway-client|backend|operator|operator.read,operator.admin|123|token|nonce-xyz"
|
|
)
|
|
|
|
|
|
def test_sign_device_payload_produces_valid_ed25519_signature(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path,
|
|
) -> None:
|
|
identity_path = tmp_path / "identity" / "device.json"
|
|
monkeypatch.setenv("OPENCLAW_GATEWAY_DEVICE_IDENTITY_PATH", str(identity_path))
|
|
identity = load_or_create_device_identity()
|
|
|
|
payload = "v1|device|client|backend|operator|operator.read|1|token"
|
|
signature = sign_device_payload(identity.private_key_pem, payload)
|
|
|
|
loaded = serialization.load_pem_public_key(identity.public_key_pem.encode("utf-8"))
|
|
assert isinstance(loaded, Ed25519PublicKey)
|
|
loaded.verify(_base64url_decode(signature), payload.encode("utf-8"))
|