| 1 | diff --git a/internal/webui/browse_test.go b/internal/webui/browse_test.go |
| 2 | index a3a9d3f..6c76094 100644 |
| 3 | --- a/internal/webui/browse_test.go |
| 4 | +++ b/internal/webui/browse_test.go |
| 5 | @@ -72,7 +72,7 @@ func TestBrowseRoutesUseObjectContractsAndRenderData(t *testing.T) { |
| 6 | |
| 7 | func TestConnectPublishesGRASPAndDeliveryKinds(t *testing.T) { |
| 8 | b := &fakeBackend{policy: policy.Defaults(strings.Repeat("a", 64))} |
| 9 | - app, err := New(b, Options{}) |
| 10 | + app, err := New(b, Options{Actor: func(*http.Request) (string, error) { return b.policy.Owner, nil }}) |
| 11 | if err != nil { |
| 12 | t.Fatal(err) |
| 13 | } |
| 14 | diff --git a/internal/webui/manage.html b/internal/webui/manage.html |
| 15 | index ad9c83b..c481c02 100644 |
| 16 | --- a/internal/webui/manage.html |
| 17 | +++ b/internal/webui/manage.html |
| 18 | @@ -116,9 +116,9 @@ |
| 19 | <label>Blossom server URLs, one per line <textarea name="lines"></textarea></label> |
| 20 | <button>Publish server list</button> |
| 21 | </publish-list> |
| 22 | -<h3>Sign in</h3> |
| 23 | +{{if not .Actor}}<h3>Sign in</h3> |
| 24 | {{template "signerControls" .}} |
| 25 | -</section> |
| 26 | +{{end}}</section> |
| 27 | {{end}} |
| 28 | |
| 29 | {{define "data"}}<section> |
| 30 | diff --git a/internal/webui/public.html b/internal/webui/public.html |
| 31 | index 935f878..e87a385 100644 |
| 32 | --- a/internal/webui/public.html |
| 33 | +++ b/internal/webui/public.html |
| 34 | @@ -18,11 +18,13 @@ |
| 35 | |
| 36 | {{define "signin"}}<section> |
| 37 | <h1>{{if .Actor}}Your account{{else}}Sign in{{end}}</h1> |
| 38 | +{{if .Notice}}<p role="status">{{.Notice}}</p>{{end}} |
| 39 | {{if .Actor}}<p>Signed in as {{template "nostrKey" .Actor}}.</p> |
| 40 | -<p><a href="/manage/people">Manage relay</a> | <a href="/tools">Browser tools</a></p> |
| 41 | +<p><a href="/manage/people">Manage relay</a> | <a href="/tools">Browser tools</a> | <a href="/inbox">Inbox</a></p> |
| 42 | +<p>Use the sign out control in the footer to end this session.</p> |
| 43 | {{else}}<p>Use a browser extension, or connect Amber on your phone. The relay never sees a private key.</p> |
| 44 | -{{end}}{{template "signerControls" .}} |
| 45 | -</section> |
| 46 | +{{template "signerControls" .}} |
| 47 | +{{end}}</section> |
| 48 | {{end}} |
| 49 | |
| 50 | {{define "signerControls"}}<p><button id="session-login" type="button">Sign in with connected signer</button></p> |
| 51 | diff --git a/internal/webui/shell_test.go b/internal/webui/shell_test.go |
| 52 | index 3045313..edfc743 100644 |
| 53 | --- a/internal/webui/shell_test.go |
| 54 | +++ b/internal/webui/shell_test.go |
| 55 | @@ -27,7 +27,7 @@ func (b *siteBackend) Query(_ context.Context, method string, params []json.RawM |
| 56 | |
| 57 | func TestShellRendersRailPanelAndPrompt(t *testing.T) { |
| 58 | backend := &fakeBackend{policy: policy.Defaults(strings.Repeat("a", 64))} |
| 59 | - app, err := New(backend, Options{}) |
| 60 | + app, err := New(backend, Options{Actor: func(*http.Request) (string, error) { return backend.policy.Owner, nil }}) |
| 61 | if err != nil { |
| 62 | t.Fatal(err) |
| 63 | } |
| 64 | @@ -156,3 +156,19 @@ func TestFilePagePreviewsImages(t *testing.T) { |
| 65 | t.Fatalf("no image preview: %s", body) |
| 66 | } |
| 67 | } |
| 68 | + |
| 69 | +func TestGuestsSeeSignInInsteadOfManagementPages(t *testing.T) { |
| 70 | + backend := &fakeBackend{policy: policy.Defaults(strings.Repeat("a", 64))} |
| 71 | + app, err := New(backend, Options{Actor: func(*http.Request) (string, error) { return "", nil }}) |
| 72 | + if err != nil { |
| 73 | + t.Fatal(err) |
| 74 | + } |
| 75 | + for _, path := range []string{"/manage/people", "/manage/owner", "/manage/status", "/manage/data"} { |
| 76 | + recorder := httptest.NewRecorder() |
| 77 | + app.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, path, nil)) |
| 78 | + body := recorder.Body.String() |
| 79 | + if recorder.Code != http.StatusOK || !strings.Contains(body, "Sign in to manage this relay.") || strings.Contains(body, `<rpc-form method="setmember"`) || strings.Contains(body, `id="rail"><div id="railbox"><header><span><a href="/">`) { |
| 80 | + t.Fatalf("%s exposed management content to a guest: %d %s", path, recorder.Code, body[:min(600, len(body))]) |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | diff --git a/internal/webui/signin_test.go b/internal/webui/signin_test.go |
| 85 | index 204e115..2c96447 100644 |
| 86 | --- a/internal/webui/signin_test.go |
| 87 | +++ b/internal/webui/signin_test.go |
| 88 | @@ -23,9 +23,12 @@ func TestSignInNavigationAndAccount(t *testing.T) { |
| 89 | if actor != "" && (!strings.Contains(body, `title="`+actor+`"`) || strings.Count(body, `id="session-logout"`) != 1) { |
| 90 | t.Fatalf("%s missing account or unique sign-out control", path) |
| 91 | } |
| 92 | - if path == "/signin" && (!strings.Contains(body, `id="nostrconnect"`) || strings.Contains(body, `method="setconnections"`)) { |
| 93 | + if path == "/signin" && actor == "" && (!strings.Contains(body, `id="nostrconnect"`) || strings.Contains(body, `method="setconnections"`)) { |
| 94 | t.Fatal("sign-in page must show signer controls without connection settings") |
| 95 | } |
| 96 | + if actor != "" && strings.Contains(body, `id="nostrconnect"`) { |
| 97 | + t.Fatalf("%s shows signer controls to a signed-in account", path) |
| 98 | + } |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | diff --git a/internal/webui/webui.go b/internal/webui/webui.go |
| 103 | index ef0d3ab..03f900e 100644 |
| 104 | --- a/internal/webui/webui.go |
| 105 | +++ b/internal/webui/webui.go |
| 106 | @@ -631,6 +631,12 @@ func (a *App) render(writer http.ResponseWriter, request *http.Request, data Pag |
| 107 | } |
| 108 | data.Path = strings.TrimPrefix(request.URL.Path, data.Base) |
| 109 | a.privatePageData(&data, request, actor) |
| 110 | + if !data.Private && data.Actor == "" && railKind(data.Tab) == "manage" && data.Tab != "tools" { |
| 111 | + // Management pages are for signed-in people only. Guests see the |
| 112 | + // sign-in page at the same address and come back after signing in. |
| 113 | + data.Tab, data.Notice = "signin", "Sign in to manage this relay." |
| 114 | + data.Event, data.Feed, data.Error = nil, nil, "" |
| 115 | + } |
| 116 | writer.Header().Set("Cache-Control", "private, no-store") |
| 117 | writer.Header().Set("content-type", "text/html; charset=utf-8") |
| 118 | var rendered bytes.Buffer |
| 119 | diff --git a/internal/webui/webui_test.go b/internal/webui/webui_test.go |
| 120 | index e25cfb3..837df47 100644 |
| 121 | --- a/internal/webui/webui_test.go |
| 122 | +++ b/internal/webui/webui_test.go |
| 123 | @@ -221,7 +221,7 @@ func TestRPCUsesVerifiedActorAndBackendContract(t *testing.T) { |
| 124 | |
| 125 | func TestSignerBundleAndDedicatedJourneysArePresent(t *testing.T) { |
| 126 | backend := &fakeBackend{policy: policy.Defaults(strings.Repeat("a", 64))} |
| 127 | - app, err := New(backend, Options{Actor: func(*http.Request) (string, error) { return backend.policy.Owner, nil }}) |
| 128 | + app, err := New(backend, Options{Actor: func(*http.Request) (string, error) { return "", nil }}) |
| 129 | if err != nil { |
| 130 | t.Fatal(err) |
| 131 | } |