oficina / memoria Public

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

Plain git — no account needed to clone.

Download

Download this file

1package memoria
2
3import (
4 "bytes"
5 "encoding/base64"
6 "encoding/json"
7 "net/http"
8 "strings"
9 "testing"
10
11 assertioncrypto "amadan.net/rastrillo/rastrillo/crypto"
12)
13
14const homeTestDirectory = "https://directory.example.test"
15
16// newHomeConsumerFixture loads Home the way production does, through
17// LoadConfig from the environment, so HOME_DIRECTORY_ORIGIN (unprefixed,
18// the name Home itself reads) is read exactly as it is live.
19func newHomeConsumerFixture(t *testing.T, directory string) *homeRouterFixture {
20 t.Helper()
21 key, err := assertioncrypto.Generate()
22 if err != nil {
23 t.Fatal(err)
24 }
25 manifest, _ := json.Marshal(map[string]string{"key": base64.StdEncoding.EncodeToString(key.SignPub())})
26 env := map[string]string{
27 "MEMORIA_DEK": base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{2}, 32)),
28 "MEMORIA_INSTANCE_KEY": "test-only-instance",
29 "MEMORIA_OWNERS": "alice@example.test",
30 "MEMORIA_ALLOW_LOGGED_MAIL": "1",
31 "MEMORIA_ORIGIN": homeTestOrigin,
32 "MEMORIA_HOME_ISSUER": homeTestIssuer,
33 "MEMORIA_HOME_CONSUMER_ID": "memoria-test",
34 "MEMORIA_HOME_KEYS_BASE64": base64.StdEncoding.EncodeToString(manifest),
35 "MEMORIA_HOME_DEK_BASE64": base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{7}, 32)),
36 "HOME_DIRECTORY_ORIGIN": directory,
37 }
38 cfg, err := LoadConfig(func(k string) string { return env[k] })
39 if err != nil || cfg.Home == nil {
40 t.Fatalf("LoadConfig: %v", err)
41 }
42 return newHomeRouterFixtureWith(t, key, cfg)
43}
44
45// Sign out in Memoria ends the id.* session too: the app session ends and
46// the same POST is handed on (307) to {issuer}/signout, which returns the
47// browser to /signed-out. Without it the next "Sign in" passed silently
48// through the live id.* session (spec shared-chrome §9.5).
49func TestHomeSignoutChainsToIssuer(t *testing.T) {
50 f := newHomeConsumerFixture(t, "")
51 f.local(t)
52 w := f.do("POST", "/signout", "")
53 if w.Code != http.StatusTemporaryRedirect || w.Header().Get("Location") != homeTestIssuer+"/signout" {
54 t.Fatalf("POST /signout: status %d Location %q, want 307 to %s/signout", w.Code, w.Header().Get("Location"), homeTestIssuer)
55 }
56 if got := w.Header().Get("Referrer-Policy"); got != "strict-origin-when-cross-origin" {
57 t.Fatalf("Referrer-Policy %q: id.* admits the null-Origin hop by Referer", got)
58 }
59 if f.cookies[f.app.home.sessions.CookieName()] != nil {
60 t.Fatal("sign-out kept the Memoria session cookie")
61 }
62}
63
64// A signed-in person who follows a "Sign in" link goes straight to where
65// they were headed, with no second exchange.
66func TestHomeSigninWhenSignedInReturnsToPage(t *testing.T) {
67 f := newHomeConsumerFixture(t, "")
68 f.local(t)
69 if got := homeRedirect(t, f.do("GET", "/signin?return_to=%2Fconnectors", "")); got != "/connectors" {
70 t.Fatalf("signed-in /signin went to %q, want /connectors", got)
71 }
72}
73
74// "All apps" goes to the hub directory, from HOME_DIRECTORY_ORIGIN, on
75// every signed-in page, and is absent when no directory is configured.
76func TestAllAppsLinksToDirectory(t *testing.T) {
77 link := `href="` + homeTestDirectory + `/"`
78 f := newHomeConsumerFixture(t, homeTestDirectory)
79 f.local(t)
80 for _, path := range []string{"/", "/connectors", "/help/open-source"} {
81 body := f.do("GET", path, "").Body.String()
82 if !strings.Contains(body, link) || !strings.Contains(body, ">All apps<") {
83 t.Fatalf("GET %s: no All apps link to %s/", path, homeTestDirectory)
84 }
85 }
86
87 bare := newHomeConsumerFixture(t, "")
88 bare.local(t)
89 if body := bare.do("GET", "/", "").Body.String(); strings.Contains(body, "All apps") {
90 t.Fatal("All apps shown with no directory configured")
91 }
92}
93
94// A signed-out visit to a page in Home mode goes to sign-in carrying the
95// page, and Home sign-in comes back to that exact page, not the root.
96func TestHomeSigninReturnsToTheExactPage(t *testing.T) {
97 f := newHomeConsumerFixture(t, "")
98 f.local(t)
99 if w := f.do("GET", "/account/home", ""); w.Code != http.StatusOK {
100 t.Fatal("link page", w.Code)
101 }
102 start := homeRedirect(t, f.do("POST", "/account/home", ""))
103 if homeRedirect(t, f.exchange(t, start, "opaque-home-alice")) != "/account/home/confirm" {
104 t.Fatal("wrong proof landing")
105 }
106 homeRedirect(t, f.do("POST", "/account/home/confirm", ""))
107 f.do("POST", "/signout", "")
108
109 signin := homeRedirect(t, f.do("GET", "/connectors", ""))
110 if signin != "/signin?return_to=%2Fconnectors" {
111 t.Fatalf("signed-out /connectors went to %q, want /signin?return_to=%%2Fconnectors", signin)
112 }
113 start = homeRedirect(t, f.do("GET", signin, ""))
114 if got := homeRedirect(t, f.exchange(t, start, "opaque-home-alice")); got != "/connectors" {
115 t.Fatalf("Home sign-in landed on %q, want /connectors", got)
116 }
117}
118