e2e/adminusersweb_test.go
85 lines · 3654 bytes
1 symbol in this file
1package e2e
2
3import (
4 "net/url"
5 "strings"
6 "testing"
7)
8
9// /admin/users lists accounts and runs the account commands, which the
10// web can reach now that nothing is held back from it (#234). Demote
11// and disable carry the typed-name check.
12func TestAdminUsersWeb(t *testing.T) {
13 t.Parallel()
14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
15 rootKey := inst.newKey(t, "root")
16 aliceKey := inst.newKey(t, "alice")
17 inst.admin(t, "admin", "user", "create", "root", "--key", rootKey+".pub", "--admin",
18 "--email", "root@example.test", "--verified")
19 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub",
20 "--email", "alice@example.test", "--verified")
21
22 // A non-admin sees neither the page nor a hint that it exists.
23 if status, _ := browserGet(t, inst.login(t, aliceKey), inst.base()+"/admin/users"); status != 404 {
24 t.Fatalf("non-admin reached the account list: %d", status)
25 }
26
27 root := inst.login(t, rootKey)
28 page := inst.base() + "/admin/users"
29 _, body := browserGet(t, root, page)
30 for _, want := range []string{">alice<", ">root<", "Promote", "Disable"} {
31 if !strings.Contains(body, want) {
32 t.Fatalf("account list missing %q:\n%s", want, body)
33 }
34 }
35 // The admin page links here.
36 if _, admin := browserGet(t, root, inst.base()+"/admin"); !strings.Contains(admin, `href="/admin/users"`) {
37 t.Fatalf("admin page does not link the account list:\n%s", admin)
38 }
39
40 post := func(v url.Values) string {
41 t.Helper()
42 status, body := browserPost(t, root, page, v)
43 if status != 200 {
44 t.Fatalf("post %v: %d", v, status)
45 }
46 return body
47 }
48
49 // Disable needs the typed name: the wrong one changes nothing.
50 post(url.Values{"field": {"disable"}, "user": {"alice"}, "confirm": {"alicce"}})
51 if out, _, _ := inst.ssh(t, rootKey, "", "admin", "user", "show", "alice", "--json"); !strings.Contains(out, `"state":"active"`) {
52 t.Fatalf("a mistyped confirm still disabled the account: %s", out)
53 }
54 post(url.Values{"field": {"disable"}, "user": {"alice"}, "confirm": {"alice"}})
55 if out, _, _ := inst.ssh(t, rootKey, "", "admin", "user", "show", "alice", "--json"); !strings.Contains(out, `"state":"disabled"`) {
56 t.Fatalf("disable did not take: %s", out)
57 }
58 post(url.Values{"field": {"enable"}, "user": {"alice"}})
59 post(url.Values{"field": {"promote"}, "user": {"alice"}})
60 if out, _, _ := inst.ssh(t, rootKey, "", "admin", "user", "show", "alice", "--json"); strings.Contains(out, `"admin":true`) {
61 t.Fatalf("promote without a confirm took: %s", out)
62 }
63 post(url.Values{"field": {"promote"}, "user": {"alice"}, "confirm": {"alice"}})
64 if out, _, _ := inst.ssh(t, rootKey, "", "admin", "user", "show", "alice", "--json"); !strings.Contains(out, `"admin":true`) {
65 t.Fatalf("promote did not take: %s", out)
66 }
67 post(url.Values{"field": {"demote"}, "user": {"alice"}, "confirm": {"alice"}})
68 if out, _, _ := inst.ssh(t, rootKey, "", "admin", "user", "show", "alice", "--json"); strings.Contains(out, `"admin":true`) {
69 t.Fatalf("demote did not take: %s", out)
70 }
71
72 // The command's own refusals reach the page: the last admin stays.
73 b := post(url.Values{"field": {"demote"}, "user": {"root"}, "confirm": {"root"}})
74 if !strings.Contains(b, "admin") {
75 t.Fatalf("no message after demoting the last admin:\n%s", b)
76 }
77 if out, _, _ := inst.ssh(t, rootKey, "", "admin", "user", "show", "root", "--json"); !strings.Contains(out, `"admin":true`) {
78 t.Fatalf("the last admin was demoted: %s", out)
79 }
80
81 // The state filter narrows the list.
82 if _, body := browserGet(t, root, page+"?state=admin"); strings.Contains(body, ">alice<") {
83 t.Fatalf("the admin filter listed a non-admin:\n%s", body)
84 }
85}