aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests')
-rw-r--r--packages/meshbay-hub/tests/test_account_deletion.py13
-rw-r--r--packages/meshbay-hub/tests/test_bundle_pepper.py39
-rw-r--r--packages/meshbay-hub/tests/test_device_auth.py24
-rw-r--r--packages/meshbay-hub/tests/test_password_reset.py3
4 files changed, 67 insertions, 12 deletions
diff --git a/packages/meshbay-hub/tests/test_account_deletion.py b/packages/meshbay-hub/tests/test_account_deletion.py
index a31aaff..632c133 100644
--- a/packages/meshbay-hub/tests/test_account_deletion.py
+++ b/packages/meshbay-hub/tests/test_account_deletion.py
@@ -140,7 +140,8 @@ async def test_deletion_clears_device_keys(client, db_session):
select(User.id).where(User.username == "devicer_test"))).scalar_one()
r = await client.post("/v1/users/devices", headers=headers,
- json={"pk_auth_ed25519": _device_pk(), "label": "desktop"})
+ json={"pk_auth_ed25519": _device_pk(), "label": "desktop",
+ "auth_key": _auth_key(password, "devicer_test")})
assert r.status_code == 201, r.text
# Present before, or the emptiness asserted below proves nothing.
@@ -166,15 +167,19 @@ async def test_a_new_account_can_reuse_the_deleted_accounts_device(client):
"""
pk = _device_pk()
token, password = await _register(client, "firstlife")
- r = await client.post("/v1/users/devices", json={"pk_auth_ed25519": pk},
+ r = await client.post("/v1/users/devices",
+ json={"pk_auth_ed25519": pk,
+ "auth_key": _auth_key(password, "firstlife")},
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},
+ token2, password2 = await _register(client, "secondlife")
+ r = await client.post("/v1/users/devices",
+ json={"pk_auth_ed25519": pk,
+ "auth_key": _auth_key(password2, "secondlife")},
headers={"Authorization": f"Bearer {token2}"})
assert r.status_code == 201, r.text
diff --git a/packages/meshbay-hub/tests/test_bundle_pepper.py b/packages/meshbay-hub/tests/test_bundle_pepper.py
index be9da19..6c9f63d 100644
--- a/packages/meshbay-hub/tests/test_bundle_pepper.py
+++ b/packages/meshbay-hub/tests/test_bundle_pepper.py
@@ -57,7 +57,8 @@ async def test_a_device_sign_in_gets_it_too(client):
login = await _login(client, "pepper_dev")
sk = Ed25519PrivateKey.generate()
r = await client.post("/v1/users/devices", headers=_bearer(login["access_token"]),
- json={"pk_auth_ed25519": pk_to_b64(sk.public_key()), "label": ""})
+ json={"pk_auth_ed25519": pk_to_b64(sk.public_key()), "label": "",
+ "auth_key": KEY})
assert r.status_code == 201
ts = int(time.time())
sig = sk.sign(f"meshbay:user_auth:pepper_dev:{ts}".encode())
@@ -88,23 +89,51 @@ async def test_it_is_in_no_token(client):
assert not any("pepper" in k for k in claims)
+async def _ask(client, token, auth_key=None):
+ body = {} if auth_key is None else {"auth_key": auth_key}
+ return await client.post("/v1/users/me/bundle-pepper", headers=_bearer(token), json=body)
+
+
@pytest.mark.asyncio
-async def test_an_open_session_may_ask_and_a_node_may_not(client):
+async def test_a_session_asks_with_the_passphrase_and_a_node_may_not(client):
from test_node_scope_not_admin import _node_token
await _register(client, "pepper_node")
login = await _login(client, "pepper_node")
- r = await client.get("/v1/users/me/bundle-pepper", headers=_bearer(login["access_token"]))
+ r = await _ask(client, login["access_token"], KEY)
assert r.status_code == 200
assert r.json()["bundle_pepper"] == login["bundle_pepper"]
node = await _node_token(client, "pepper_node", login["access_token"])
- r = await client.get("/v1/users/me/bundle-pepper", headers=_bearer(node))
+ r = await _ask(client, node, KEY)
assert r.status_code == 403
assert login["bundle_pepper"] not in r.text
@pytest.mark.asyncio
+async def test_a_token_alone_never_gets_it(client):
+ """
+ Not a refreshed one, not the one sign-in handed out: a token is what a page
+ holds, and a pepper beside the bundles an operator keeps is the offline
+ passphrase oracle again. Two calls — refresh, then ask — must not add up
+ to what a refresh is refused.
+ """
+ await _register(client, "pepper_bare")
+ login = await _login(client, "pepper_bare")
+ refreshed = (await client.post("/v1/users/token/refresh",
+ json={"refresh_token": login["refresh_token"]})).json()
+
+ for token in (login["access_token"], refreshed["access_token"]):
+ bare = await _ask(client, token)
+ wrong = await _ask(client, token, "w" * 44)
+ assert bare.status_code == 422
+ assert wrong.status_code == 403
+ assert login["bundle_pepper"] not in bare.text + wrong.text
+ assert (await client.get("/v1/users/me/bundle-pepper",
+ headers=_bearer(login["access_token"]))).status_code in (404, 405)
+
+
+@pytest.mark.asyncio
async def test_it_is_sealed_at_rest_and_bound_to_its_account(client, db_session):
await _register(client, "pepper_rest")
login = await _login(client, "pepper_rest")
@@ -157,7 +186,7 @@ async def test_it_is_never_logged(client):
try:
await _register(client, "pepper_log")
login = await _login(client, "pepper_log")
- await client.get("/v1/users/me/bundle-pepper", headers=_bearer(login["access_token"]))
+ await _ask(client, login["access_token"], KEY)
finally:
root.removeHandler(grab)
root.setLevel(saved[0])
diff --git a/packages/meshbay-hub/tests/test_device_auth.py b/packages/meshbay-hub/tests/test_device_auth.py
index f9b172e..04012ee 100644
--- a/packages/meshbay-hub/tests/test_device_auth.py
+++ b/packages/meshbay-hub/tests/test_device_auth.py
@@ -46,10 +46,11 @@ async def _account(client, username="alice_test") -> str:
return resp.json()["access_token"]
-async def _register_device(client, token: str, pk: str, label: str = ""):
+async def _register_device(client, token: str, pk: str, label: str = "",
+ auth_key: str = "k" * 44):
return await client.post(
"/v1/users/devices",
- json={"pk_auth_ed25519": pk, "label": label},
+ json={"pk_auth_ed25519": pk, "label": label, "auth_key": auth_key},
headers={"Authorization": f"Bearer {token}"})
@@ -275,3 +276,22 @@ async def test_the_hub_states_a_minimum_client_version(client):
assert resp.status_code == 200
client_floor = resp.json()["client"]
assert client_floor["minimum"] and client_floor["recommended"]
+
+
+async def test_a_session_alone_cannot_register_a_device(client):
+ """
+ A registered key signs in with no passphrase for as long as it stays, and
+ each of those sign-ins carries the bundle pepper. So a token — refreshed,
+ or lifted from a page — must not be enough to add one: the passphrase is.
+ """
+ token = await _account(client)
+ sk, pk = _device()
+
+ bare = await client.post("/v1/users/devices", json={"pk_auth_ed25519": pk},
+ headers={"Authorization": f"Bearer {token}"})
+ wrong = await _register_device(client, token, pk, auth_key="w" * 44)
+
+ assert bare.status_code == 422
+ assert wrong.status_code == 403
+ assert (await client.post("/v1/users/auth",
+ json=_sign(sk, "alice_test"))).status_code == 401
diff --git a/packages/meshbay-hub/tests/test_password_reset.py b/packages/meshbay-hub/tests/test_password_reset.py
index b07f77c..f285c1d 100644
--- a/packages/meshbay-hub/tests/test_password_reset.py
+++ b/packages/meshbay-hub/tests/test_password_reset.py
@@ -159,7 +159,8 @@ async def test_reset_revokes_sessions_and_wipes_devices(client, db_session):
sk = Ed25519PrivateKey.generate()
dev = await client.post(
"/v1/users/devices",
- json={"pk_auth_ed25519": pk_to_b64(sk.public_key()), "label": "laptop"},
+ json={"pk_auth_ed25519": pk_to_b64(sk.public_key()), "label": "laptop",
+ "auth_key": "erin_test" + "a" * 40},
headers={"Authorization": f"Bearer {token}"})
assert dev.status_code == 201, dev.text