aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-cli/tests/test_membership.py
blob: 30fe680200f34a65bea8ea64ec90a0b87faa6f9c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
"""
The pieces `join`, `devices` and `browser-access` stand on, held to the
application's own rules: the invitation link's shape (`static/invite-link.js`),
how a typed code is folded (`crypto.js` `normalizeCode`), and which bundle on a
node is current enough to be left alone.
"""

import base64

import pytest
from meshbay_cli.client.devices import find_device, normalize_code
from meshbay_cli.client.invite import parse_link
from meshbay_cli.client.keys import bundle_is_current, open_bundle, seal_bundle
from meshbay_cli.errors import NOT_FOUND, USAGE, CliError

GROUP = "0f8fad5b-d9cb-469f-a165-70867728950e"
TICKET = "AbCdEfGhIjKlMnOpQr-_12"
NODE_URLSAFE = base64.urlsafe_b64encode(bytes(range(250, 256)) * 5 + b"\xfe\xff").decode()


def _link(**over) -> str:
    q = {"v": "1", "g": GROUP, "t": TICKET, "n": NODE_URLSAFE.rstrip("="), "c": "k7p2-9wqx",
         **over}
    return "https://hub.test/#/invite?" + "&".join(f"{k}={v}" for k, v in q.items())


def test_a_link_gives_its_hub_group_ticket_node_and_code():
    link = parse_link(_link())
    assert (link.origin, link.group_id, link.ticket, link.code) == (
        "https://hub.test", GROUP, TICKET, "K7P2-9WQX")
    # The node key as the hub lists nodes: standard base64, padded.
    assert base64.b64decode(link.node_pk) == base64.urlsafe_b64decode(NODE_URLSAFE)
    assert "+" in link.node_pk or "/" in link.node_pk
    # Only the fragment, as somebody may paste it.
    assert parse_link(_link().split("#", 1)[1]).origin == ""


@pytest.mark.parametrize("bad", [{"v": "2"}, {"g": "not-a-group"}, {"t": "short"},
                                 {"n": "x" * 42}, {"c": "K7P2-9WQ"}, {"c": "K7P2+9WQX"}])
def test_a_malformed_link_is_no_link(bad):
    assert parse_link(_link(**bad)) is None


def test_text_without_a_link_is_no_link():
    assert parse_link("Family@alice") is None


def test_a_code_is_folded_as_the_application_folds_it():
    assert normalize_code("k7p2-9wqx") == "K7P29WQX"
    assert normalize_code(" ilou 0O1V ") == "110V001V"


def test_a_device_is_named_by_an_unambiguous_prefix():
    devices = [{"pk_ed25519": "ABCDEFGH1234"}, {"pk_ed25519": "ABCDEFGH5678"}]
    assert find_device(devices, "ABCDEFGH5")["pk_ed25519"] == "ABCDEFGH5678"
    with pytest.raises(CliError) as e:
        find_device(devices, "ABCDEFGH")
    assert e.value.code == USAGE
    with pytest.raises(CliError) as e:
        find_device(devices, "ABCD")
    assert e.value.code == USAGE
    with pytest.raises(CliError) as e:
        find_device(devices, "ZZZZZZZZ")
    assert e.value.code == NOT_FOUND


def test_a_bundle_sealed_here_opens_and_is_current_only_under_its_own_key():
    keys = {"skEd": base64.b64encode(b"ed").decode(), "skX": base64.b64encode(b"x").decode()}
    m, other = b"\x01" * 32, b"\x02" * 32
    sealed = seal_bundle(keys, master=m, pepper_version=1, user_id="u", node_pk="pk")
    assert base64.b64decode(sealed)[:5] == b"MBK3\x01"
    assert open_bundle(sealed, master=m, legacy=None, user_id="u", node_pk="pk") == keys
    assert bundle_is_current(sealed, master=m, user_id="u", node_pk="pk")
    # Sealed under an earlier passphrase, or for another node: sealed again.
    assert not bundle_is_current(sealed, master=other, user_id="u", node_pk="pk")
    assert not bundle_is_current(sealed, master=m, user_id="u", node_pk="pk2")
    legacy = base64.b64encode(b"MBK2" + b"\x00" * 40).decode()
    assert not bundle_is_current(legacy, master=m, user_id="u", node_pk="pk")