Phase 3 of plan.md. Device registration with one-time secrets, scoped single-use socket tickets, the lock-owner WS /nodes/ws with handshake and heartbeat eviction, a single batch ConsentGate, and NodesService which disconnects nodes within one tick when consent, device or the nodes_enabled kill switch changes. A reconcile of 2000 sessions measured about 110 ms after replacing per-session queries with batches. Devii may withdraw but never grant bandwidth_sharing. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
68 lines
2.7 KiB
Python
68 lines
2.7 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
|
|
from devplacepy.database import set_consent
|
|
from devplacepy.database.nodes import LIMITS, suspend_device
|
|
from devplacepy.services.messaging.tickets import redeem_ticket, redeem_ticket_row
|
|
from tests.api.nodes.devices import create
|
|
|
|
|
|
def bearer(secret):
|
|
return {"Authorization": f"Bearer {secret}"}
|
|
|
|
|
|
def registered(client, member):
|
|
body = create(client, member).json()
|
|
return body["device"]["uid"], body["secret"]
|
|
|
|
|
|
def test_no_credentials_or_a_wrong_secret_is_401(client):
|
|
assert client.post("/nodes/ticket").status_code == 401
|
|
assert client.post("/nodes/ticket", headers=bearer("nope")).status_code == 401
|
|
assert client.post("/nodes/ticket", headers=bearer("dpn_unknown")).status_code == 401
|
|
|
|
|
|
def test_a_session_api_key_is_not_a_device_secret(client, member):
|
|
assert client.post("/nodes/ticket", headers=member["headers"]).status_code == 401
|
|
|
|
|
|
def test_a_valid_secret_gets_a_short_single_use_ticket(client, member):
|
|
device_uid, secret = registered(client, member)
|
|
response = client.post("/nodes/ticket", headers=bearer(secret))
|
|
assert response.status_code == 200
|
|
assert response.headers["cache-control"] == "no-store"
|
|
body = response.json()
|
|
assert body["expires_in"] == LIMITS.ticket_ttl_seconds
|
|
assert body["ws_path"] == "/nodes/ws"
|
|
row = redeem_ticket_row(body["ticket"], "nodes")
|
|
assert row["device_uid"] == device_uid and row["user_uid"] == member["uid"]
|
|
assert redeem_ticket_row(body["ticket"], "nodes") is None
|
|
|
|
|
|
def test_a_node_ticket_cannot_open_a_chat_socket(client, member):
|
|
_, secret = registered(client, member)
|
|
ticket = client.post("/nodes/ticket", headers=bearer(secret)).json()["ticket"]
|
|
assert redeem_ticket(ticket) is None
|
|
assert redeem_ticket_row(ticket, "nodes") is not None
|
|
|
|
|
|
def test_withdrawing_consent_stops_new_tickets(client, member):
|
|
_, secret = registered(client, member)
|
|
set_consent("user", member["uid"], "bandwidth_sharing", False)
|
|
response = client.post("/nodes/ticket", headers=bearer(secret))
|
|
assert response.status_code == 403
|
|
assert response.json()["error"]["reason"] == "no_consent"
|
|
|
|
|
|
def test_a_suspended_device_gets_no_ticket(client, member):
|
|
device_uid, secret = registered(client, member)
|
|
suspend_device(device_uid, "test", 30)
|
|
response = client.post("/nodes/ticket", headers=bearer(secret))
|
|
assert response.status_code == 403
|
|
assert response.json()["error"]["reason"] == "suspended"
|
|
|
|
|
|
def test_a_deleted_device_secret_stops_working(client, member):
|
|
device_uid, secret = registered(client, member)
|
|
client.post(f"/nodes/devices/{device_uid}/delete", headers=member["headers"])
|
|
assert client.post("/nodes/ticket", headers=bearer(secret)).status_code == 401
|