oficina / slides Public

Clone
git clone https://amadan.net/oficina/slides

Plain git — no account needed to clone.

Download

Download this file

1package slides
2
3import (
4 "bytes"
5 "encoding/base64"
6 "encoding/json"
7 "fmt"
8 "html"
9 "net/http"
10 "net/http/httptest"
11 "net/url"
12 "os"
13 "regexp"
14 "strings"
15 "testing"
16 "time"
17
18 "amadan.net/oficina/home/connect"
19 "amadan.net/oficina/slides/internal/slides/keymailtest"
20 assertioncrypto "amadan.net/rastrillo/rastrillo/crypto"
21)
22
23// TestMain keeps Keymail classification inside the process for this
24// package's tests: every DNS query is answered NXDOMAIN and only the
25// Keymail fixtures a test registers answer HTTPS (keymailtest).
26func TestMain(m *testing.M) {
27 restore, err := keymailtest.Install()
28 if err != nil {
29 fmt.Fprintln(os.Stderr, "keymailtest.Install:", err)
30 os.Exit(1)
31 }
32 code := m.Run()
33 // No test may leave a sign-in mail worker running: every app and
34 // every queue a test builds is closed in its cleanup.
35 if code == 0 {
36 deadline := time.Now().Add(5 * time.Second)
37 for mailWorkers() > 0 && time.Now().Before(deadline) {
38 time.Sleep(10 * time.Millisecond)
39 }
40 if n := mailWorkers(); n > 0 {
41 fmt.Fprintf(os.Stderr, "FAIL: %d sign-in mail queue goroutines still running after the tests\n", n)
42 code = 1
43 }
44 }
45 restore()
46 os.Exit(code)
47}
48
49// TestHomeLocalKeymailIsAContinuationAndRefusesAtTheDoor is the
50// Home-mode half of the Keymail sign-in: with Home configured, the
51// local form (/signin/local, posting to /signin under LocalGuard) sends
52// a permitted and a non-permitted Keymail address to the same
53// continuation page — never a 303 out of the form POST, whose
54// form-action is 'self' plus the Home issuer — and a non-permitted one
55// that Keymail verified is refused on the local door, not in bare text.
56func TestHomeLocalKeymailIsAContinuationAndRefusesAtTheDoor(t *testing.T) {
57 const host = "keymail.home.test"
58 member, stranger := "owner@"+host, "stranger@"+host
59 km := keymailtest.Serve(t, host, member, stranger)
60
61 key, err := assertioncrypto.Generate()
62 if err != nil {
63 t.Fatal(err)
64 }
65 raw, _ := json.Marshal(map[string]string{"test": base64.StdEncoding.EncodeToString(key.SignPub())})
66 mail := &homeTestMail{}
67 mux, a, err := New(testDB(t), Config{Mailer: mail, Env: EnvDevelopment, Origin: homeTestOrigin, Logger: quietLogger(),
68 AllowedAddresses: []string{member},
69 Home: &connect.Credentials{Issuer: "https://home.example.com", ConsumerID: "slides-test",
70 KeysBase64: base64.StdEncoding.EncodeToString(raw), DEKBase64: base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{8}, 32))}})
71 if err != nil {
72 t.Fatal(err)
73 }
74 t.Cleanup(func() { a.Close() })
75
76 do := func(method, path string, form url.Values, cookies []*http.Cookie) *httptest.ResponseRecorder {
77 r := httptest.NewRequest(method, homeTestOrigin+path, strings.NewReader(form.Encode()))
78 if method == http.MethodPost {
79 r.Header.Set("Origin", homeTestOrigin)
80 r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
81 }
82 for _, c := range cookies {
83 r.AddCookie(c)
84 }
85 w := httptest.NewRecorder()
86 mux.ServeHTTP(w, r)
87 return w
88 }
89 href := regexp.MustCompile(`href="(https://` + regexp.QuoteMeta(host) + `/oauth/authorize\?[^"]+)"`)
90 begin := func(address string) (*httptest.ResponseRecorder, *url.URL) {
91 rec := do(http.MethodPost, "/signin", url.Values{"address": {address}}, nil)
92 if rec.Code != http.StatusOK || rec.Header().Get("Location") != "" {
93 t.Fatalf("POST /signin %q in Home mode: %d %q, want 200 and the continuation page", address, rec.Code, rec.Header().Get("Location"))
94 }
95 m := href.FindStringSubmatch(rec.Body.String())
96 if m == nil || !strings.Contains(rec.Body.String(), `http-equiv="refresh"`) {
97 t.Fatalf("POST /signin %q in Home mode: no Keymail continuation:\n%s", address, rec.Body.String())
98 }
99 u, err := url.Parse(html.UnescapeString(m[1]))
100 if err != nil {
101 t.Fatal(err)
102 }
103 return rec, u
104 }
105
106 _, _ = begin(member)
107 rec, u := begin(stranger)
108 a.FlushMail()
109 if mail.body != "" {
110 t.Fatal("a Keymail address was mailed a link")
111 }
112
113 cb := do(http.MethodGet, "/auth/callback?"+url.Values{"state": {u.Query().Get("state")}, "code": {km.Approve(stranger)}}.Encode(), nil, rec.Result().Cookies())
114 // This mux has no locale middleware, so the catalog key itself is
115 // what proves the refusal sentence was chosen.
116 if body := cb.Body.String(); cb.Code != http.StatusForbidden || !strings.Contains(body, "slides.signin.error.refused") || !strings.Contains(body, `name="address"`) {
117 t.Fatalf("Home-mode callback for a non-permitted address: %d %q, want 403 and the local door with the refusal\n%s", cb.Code, cb.Header().Get("Location"), body)
118 }
119 if spoof := do(http.MethodGet, "/signin/local?err=refused", nil, nil).Body.String(); strings.Contains(spoof, "slides.signin.error.refused") {
120 t.Fatal("anybody can show the refusal by visiting /signin/local?err=refused")
121 }
122}
123