diff options
Diffstat (limited to 'packages/meshbay-hub/tests')
| -rw-r--r-- | packages/meshbay-hub/tests/test_account_deletion.py | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_account_deletion.py b/packages/meshbay-hub/tests/test_account_deletion.py index cddb5d0..b97417d 100644 --- a/packages/meshbay-hub/tests/test_account_deletion.py +++ b/packages/meshbay-hub/tests/test_account_deletion.py @@ -124,6 +124,73 @@ async def test_deletion_clears_memberships_notifications_and_tokens( assert rows == [], f"{model.__name__} survived the deletion" +def _device_pk() -> str: + from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey + from meshbay_common.crypto import pk_to_b64 + return pk_to_b64(Ed25519PrivateKey.generate().public_key()) + + +@pytest.mark.asyncio +async def test_deletion_clears_device_keys_and_swarm_sources(client, db_session): + """ + The privacy statement says every account row goes but the IP log. Swarm + sources are keyed by the *user* id despite the column's name, and carry the + node's ip:port. + """ + from meshbay_hub.db.models import SwarmSource, UserDevice + + token, password = await _register(client, "devicer") + headers = {"Authorization": f"Bearer {token}"} + uid = (await db_session.execute( + select(User.id).where(User.username == "devicer"))).scalar_one() + + r = await client.post("/v1/users/devices", headers=headers, + json={"pk_auth_ed25519": _device_pk(), "label": "desktop"}) + assert r.status_code == 201, r.text + r = await client.post("/v1/swarm/register", headers=headers, + json={"content_hash": "ab" * 32, "endpoint": "192.0.2.7:4433"}) + assert r.status_code == 201, r.text + + # Present before, or the emptiness asserted below proves nothing. + assert (await db_session.execute( + select(UserDevice).where(UserDevice.user_id == uid))).scalars().all() + assert (await db_session.execute( + select(SwarmSource).where(SwarmSource.node_id == uid))).scalars().all() + + r = await client.request("DELETE", "/v1/users/me", headers=headers, + json={"auth_key": _auth_key(password, "devicer")}) + assert r.status_code == 200, r.text + + db_session.expire_all() + assert (await db_session.execute( + select(UserDevice).where(UserDevice.user_id == uid))).scalars().all() == [] + assert (await db_session.execute( + select(SwarmSource).where(SwarmSource.node_id == uid))).scalars().all() == [] + + +@pytest.mark.asyncio +async def test_a_new_account_can_reuse_the_deleted_accounts_device(client): + """ + The desktop client keeps its device key after the account is deleted. Left + on the tombstone, that key was "another account's", and the next account + created from the same installation was refused its device with a 409 that + only reached the console. + """ + pk = _device_pk() + token, password = await _register(client, "firstlife") + r = await client.post("/v1/users/devices", json={"pk_auth_ed25519": pk}, + headers={"Authorization": f"Bearer {token}"}) + assert r.status_code == 201, r.text + await client.request("DELETE", "/v1/users/me", + headers={"Authorization": f"Bearer {token}"}, + json={"auth_key": _auth_key(password, "firstlife")}) + + token2, _ = await _register(client, "secondlife") + r = await client.post("/v1/users/devices", json={"pk_auth_ed25519": pk}, + headers={"Authorization": f"Bearer {token2}"}) + assert r.status_code == 201, r.text + + @pytest.mark.asyncio async def test_the_ip_log_survives_and_stays_attributable(client, db_session): """ |