CI: run sanitizers as root
[tinc] / test / integration / cmd_join.py
1 #!/usr/bin/env python3
2
3 """Test invite/join error conditions."""
4
5 import os
6 import shutil
7
8 from testlib import check, util
9 from testlib.log import log
10 from testlib.proc import Tinc
11 from testlib.test import Test
12
13 FAKE_INVITE = "localhost:65535/pVOZMJGm3MqTvTu0UnhMGb2cfuqygiu79MdnERnGYdga5v8C"
14
15
16 def init(ctx: Test) -> Tinc:
17     """Initialize a node."""
18
19     node = ctx.node()
20     stdin = f"""
21         init {node}
22         set Port 0
23         set Address localhost
24         set DeviceType dummy
25     """
26     node.cmd(stdin=stdin)
27     return node
28
29
30 def test_invite(foo: Tinc) -> None:
31     """Test successful 'invite'."""
32
33     foo.cmd("set", "Mode", "switch")
34     foo.cmd("set", "Broadcast", "mst")
35     foo.start()
36
37     log.info("test successful invitation")
38     out, _ = foo.cmd("invite", "quux")
39     check.is_in(f"localhost:{foo.port}/", out)
40
41     for filename in os.listdir(foo.sub("invitations")):
42         content = util.read_text(foo.sub(f"invitations/{filename}"))
43         if filename == "ed25519_key.priv":
44             check.is_in("-----BEGIN ED25519 PRIVATE KEY-----", content)
45         else:
46             check.is_in("Broadcast = mst", content)
47             check.is_in("Mode = switch", content)
48             check.is_in("Address = localhost", content)
49             check.is_in("Name = quux", content)
50             check.is_in(f"NetName = {foo}", content)
51             check.is_in(f"ConnectTo = {foo}", content)
52
53
54 def test_invite_errors(foo: Tinc) -> None:
55     """Test invite error conditions."""
56
57     log.info("invite node with tincd stopped")
58     _, err = foo.cmd("invite", "foobar", code=1)
59     check.is_in("Could not open pid file", err)
60
61     log.info("start node %s", foo)
62     foo.start()
63
64     log.info("invite without arguments")
65     _, err = foo.cmd("invite", code=1)
66     check.is_in("Not enough arguments", err)
67
68     log.info("invite with too many arguments")
69     _, err = foo.cmd("invite", "foo", "bar", code=1)
70     check.is_in("Too many arguments", err)
71
72     log.info("invite with invalid name")
73     _, err = foo.cmd("invite", "!@#", code=1)
74     check.is_in("Invalid name for node", err)
75
76     log.info("invite existing node")
77     _, err = foo.cmd("invite", foo.name, code=1)
78     check.is_in("already exists", err)
79
80     if os.name != "nt":
81         log.info("bad permissions on invitations are fixed")
82         invites = foo.sub("invitations")
83         os.chmod(invites, 0)
84         out, _ = foo.cmd("invite", "foobar")
85         check.has_prefix(out, "localhost:")
86
87         log.info("invitations directory is created with bad permissions on parent")
88         shutil.rmtree(invites)
89         os.chmod(foo.work_dir, 0o500)
90         out, _ = foo.cmd("invite", "foobar")
91         check.has_prefix(out, "localhost:")
92         check.true(os.access(invites, os.W_OK))
93
94         log.info("fully block access to configuration directory")
95         work_dir = foo.sub("test_no_access")
96         os.mkdir(work_dir, mode=0)
97         _, err = foo.cmd("-c", work_dir, "invite", "foobar", code=1)
98         check.is_in("Could not open", err)
99
100
101 def test_join_errors(foo: Tinc) -> None:
102     """Test join error conditions."""
103
104     log.info("try joining with redundant arguments")
105     _, err = foo.cmd("join", "bar", "quux", code=1)
106     check.is_in("Too many arguments", err)
107
108     log.info("try joining with existing configuration")
109     _, err = foo.cmd("join", FAKE_INVITE, code=1)
110     check.is_in("already exists", err)
111
112     log.info("try running without an invite URL")
113     work_dir = foo.sub("test_no_invite")
114     join = foo.tinc("-c", work_dir, "join")
115     _, err = join.communicate(input="")
116     check.equals(1, join.returncode)
117     check.is_in("Error while reading", err)
118
119     log.info("try using an invalid invite")
120     work_dir = foo.sub("test_invalid_invite")
121     _, err = foo.cmd("-c", work_dir, "join", FAKE_INVITE, code=1)
122     check.is_in("Could not connect to", err)
123
124     if os.name != "nt":
125         log.info("bad permissions on configuration directory are fixed")
126         work_dir = foo.sub("wd_access_test")
127         os.mkdir(work_dir, mode=400)
128         _, err = foo.cmd("-c", work_dir, "join", FAKE_INVITE, code=1)
129         check.is_in("Could not connect to", err)
130         check.true(os.access(work_dir, mode=os.W_OK))
131
132
133 with Test("run invite success tests") as context:
134     test_invite(init(context))
135
136 with Test("run invite error tests") as context:
137     test_invite_errors(init(context))
138
139 with Test("run join tests") as context:
140     test_join_errors(init(context))