e2e/websignup_test.go
94 lines · 3699 bytes
1package e2e
2
3import (
4 "fmt"
5 "net/url"
6 "os"
7 "strings"
8 "testing"
9)
10
11func TestWebSignup(t *testing.T) {
12 t.Parallel()
13 smtp := startFakeSMTP(t)
14 inst := startInstanceWith(t, fmt.Sprintf(
15 "[web]\nmode = \"accounts\"\n[registration]\nmode = \"invite\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n",
16 smtp.addr))
17
18 // The landing page advertises signup; the form renders.
19 status, body := inst.get(t, "/")
20 if status != 200 || !strings.Contains(body, `href="/register"`) {
21 t.Fatalf("landing signup link: %d", status)
22 }
23 status, body = inst.get(t, "/register")
24 if status != 200 || !strings.Contains(body, "invite-only") || !strings.Contains(body, `name="key"`) {
25 t.Fatalf("register form: %d\n%s", status, body)
26 }
27 // The login page tells a brand-new visitor how to get an account.
28 status, body = inst.get(t, "/login")
29 if status != 200 || !strings.Contains(body, `href="/register"`) || !strings.Contains(body, "invite-only") {
30 t.Fatalf("login signup hint: %d\n%s", status, body)
31 }
32
33 // Invite issued over the admin path; redeemed through the browser.
34 inst.admin(t, "admin", "invite", "--email", "erin@example.test")
35 inviteCode := extractCode(t, smtp.waitMail(t, 0))
36 key := inst.newKey(t, "erin")
37 pub, err := os.ReadFile(key + ".pub")
38 if err != nil {
39 t.Fatal(err)
40 }
41
42 browser := newBrowser(t)
43 // A garbage key re-renders the form with the error, keeping the input.
44 _, body = browserPost(t, browser, inst.base()+"/register", url.Values{
45 "username": {"erin"}, "invite": {inviteCode}, "key": {"not a key"}})
46 if !strings.Contains(body, "does not parse as an SSH public key") || !strings.Contains(body, `value="erin"`) {
47 t.Fatalf("bad key handling:\n%s", body)
48 }
49 // A bad invite fails without burning anything.
50 _, body = browserPost(t, browser, inst.base()+"/register", url.Values{
51 "username": {"erin"}, "invite": {"deadbeef"}, "key": {string(pub)}})
52 if !strings.Contains(body, "invalid or already used") {
53 t.Fatalf("bad invite:\n%s", body)
54 }
55 // The real thing: account is active immediately (invite proves the mailbox).
56 status, body = browserPost(t, browser, inst.base()+"/register", url.Values{
57 "username": {"erin"}, "invite": {inviteCode}, "key": {string(pub)}})
58 if status != 200 || !strings.Contains(body, "welcome, erin") ||
59 !strings.Contains(body, `href="/settings"`) || !strings.Contains(body, "paste the code") {
60 t.Fatalf("signup: %d\n%s", status, body)
61 }
62 out, errOut, code := inst.ssh(t, key, "", "whoami")
63 if code != 0 || !strings.Contains(out, "erin") {
64 t.Fatalf("ssh after web signup: exit %d, %s%s", code, out, errOut)
65 }
66 // The invite is burned: reusing it (fresh browser, fresh key) fails.
67 key2 := inst.newKey(t, "mallory")
68 pub2, _ := os.ReadFile(key2 + ".pub")
69 _, body = browserPost(t, newBrowser(t), inst.base()+"/register", url.Values{
70 "username": {"mallory"}, "invite": {inviteCode}, "key": {string(pub2)}})
71 if !strings.Contains(body, "invalid or already used") {
72 t.Fatalf("invite reuse:\n%s", body)
73 }
74}
75
76func TestWebSignupClosedInstance(t *testing.T) {
77 t.Parallel()
78 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
79 // Closed registration: no signup route at all, and no landing hint.
80 if status, _ := inst.get(t, "/register"); status != 404 {
81 t.Fatalf("register on closed instance: %d", status)
82 }
83 _, body := inst.get(t, "/")
84 if strings.Contains(body, `href="/register"`) {
85 t.Fatal("closed landing advertises signup")
86 }
87 _, body = inst.get(t, "/login")
88 if strings.Contains(body, `href="/register"`) {
89 t.Fatal("closed login page advertises signup")
90 }
91 if !strings.Contains(body, "not accepting new accounts") {
92 t.Fatalf("closed login page says nothing about accounts:\n%s", body)
93 }
94}