oficina / docs Public

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

Plain git — no account needed to clone.

Download

Download this file

1package docs
2
3import (
4 "encoding/json"
5 "html"
6 "net/http"
7 "net/http/httptest"
8 "net/url"
9 "os"
10 "path/filepath"
11 "strings"
12 "testing"
13
14 "github.com/keymaildev/signin"
15)
16
17func TestKeymailContinuation(t *testing.T) {
18 app := newAllowlistedTestApp(t, "alice@example.test")
19 app.handlers.origin = "https://docs.example.test"
20 destination := (&signin.Keymail{Base: "https://keymail.example.test", Origin: app.handlers.origin}).AuthorizeURL("state", "challenge", true)
21 handler := app.handlers.beginSignin(func(w http.ResponseWriter, r *http.Request) {
22 http.SetCookie(w, &http.Cookie{Name: "__Host-pending", Value: "opaque", Path: "/", Secure: true, HttpOnly: true, SameSite: http.SameSiteLaxMode})
23 http.Redirect(w, r, destination, http.StatusSeeOther)
24 })
25 rec := httptest.NewRecorder()
26 policy := homeFormPolicy("https://home.example.test")
27 rec.Header().Set("Content-Security-Policy", policy)
28 handler(rec, httptest.NewRequest("POST", "/signin", nil))
29 if rec.Code != 200 || rec.Header().Get("Location") != "" {
30 t.Fatalf("status=%d location=%q", rec.Code, rec.Header().Get("Location"))
31 }
32 if rec.Header().Get("Content-Security-Policy") != policy {
33 t.Fatal("CSP changed")
34 }
35 if rec.Header().Get("Cache-Control") != "no-store" || rec.Header().Get("Referrer-Policy") != "no-referrer" {
36 t.Fatal("continuation must not cache or disclose the referring URL")
37 }
38 cookies := rec.Result().Cookies()
39 if len(cookies) != 1 || cookies[0].Name != "__Host-pending" || cookies[0].Value != "opaque" || !cookies[0].Secure || !cookies[0].HttpOnly || cookies[0].SameSite != http.SameSiteLaxMode {
40 t.Fatalf("lost pending cookie: %v", cookies)
41 }
42 body := html.UnescapeString(rec.Body.String())
43 for _, part := range []string{`http-equiv="refresh"`, `content="0;url=` + destination + `"`, `href="` + destination + `"`} {
44 if !strings.Contains(body, part) {
45 t.Fatalf("missing continuation component %q", part)
46 }
47 }
48 if strings.Contains(body, `action="/signin"`) {
49 t.Fatal("continuation rendered another sign-in form")
50 }
51 // It is the door's card on the sky, not a bare page.
52 for _, part := range []string{"estilo-door", "estilo-sky-layer", "<span>Docs</span>"} {
53 if !strings.Contains(body, part) {
54 t.Fatalf("continuation is not the door: lacks %q", part)
55 }
56 }
57 if dir := os.Getenv("DOCS_OAUTH_BROWSER_FIXTURE"); dir != "" {
58 if err := os.MkdirAll(dir, 0700); err != nil {
59 t.Fatal(err)
60 }
61 data, err := json.Marshal(map[string]any{"body": rec.Body.String(), "headers": rec.Header(), "destination": destination})
62 if err != nil {
63 t.Fatal(err)
64 }
65 if err := os.WriteFile(filepath.Join(dir, "continuation.json"), data, 0600); err != nil {
66 t.Fatal(err)
67 }
68 }
69}
70
71func TestKeymailDestinationRefusals(t *testing.T) {
72 origin := "https://docs.example.test"
73 valid := (&signin.Keymail{Base: "https://keymail.example.test", Origin: origin}).AuthorizeURL("state", "challenge", false)
74 for _, target := range []string{
75 "javascript:alert(1)", "//keymail.example.test/oauth/authorize", strings.Replace(valid, "https:", "http:", 1),
76 strings.Replace(valid, "keymail.example.test", "user@keymail.example.test", 1), valid + "#fragment", valid + "&state=duplicate",
77 strings.Replace(valid, "/oauth/authorize", "/other", 1), strings.ReplaceAll(valid, url.QueryEscape(origin), url.QueryEscape("https://foreign.example.test")),
78 strings.Replace(valid, "S256", "plain", 1),
79 } {
80 if keymailDestination(target, origin) {
81 t.Fatalf("accepted %q", target)
82 }
83 }
84 app := newAllowlistedTestApp(t, "alice@example.test")
85 app.handlers.origin = origin
86 rec := httptest.NewRecorder()
87 app.handlers.beginSignin(func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "https://foreign.example.test", 303) })(rec, httptest.NewRequest("POST", "/signin", nil))
88 if rec.Code != 502 || rec.Header().Get("Location") != "" {
89 t.Fatalf("invalid redirect status=%d location=%q", rec.Code, rec.Header().Get("Location"))
90 }
91}
92
93func TestSigninEmailFallbackIsExplicit(t *testing.T) {
94 app := newAllowlistedTestApp(t, "alice@example.test")
95 for _, path := range []string{"/signin", "/signin?force=1&err=keymail"} {
96 rec := httptest.NewRecorder()
97 app.Handler().ServeHTTP(rec, httptest.NewRequest("GET", path, nil))
98 if rec.Code != 200 {
99 t.Fatal(rec.Code)
100 }
101 forced := strings.Contains(rec.Body.String(), `name="force" value="1"`)
102 if forced != strings.Contains(path, "force=1") {
103 t.Fatalf("email force for %s = %v", path, forced)
104 }
105 }
106}
107