oficina / sheets Public

Refuse disabled Home sessions and preserve sessions during storage faults

Paul Campbell pushed by paul@keymail.dev 1baa40e3f21b13bbb92c093b26c9cfcc89884ec2 parent a0227cd
4 files changed, +72 −3
  • internal/sheets/app.go +1 −1
  • internal/sheets/home.go +18 −0
  • internal/sheets/home_navigation_test.go +50 −0
  • internal/sheets/page.go +3 −2

CI passed — run details

diff --git a/internal/sheets/app.go b/internal/sheets/app.go
index 71f84cb..9a8052a 100644
--- a/internal/sheets/app.go
+++ b/internal/sheets/app.go
@@ -274,5 +274,5 @@ func (a *App) requireSession(next http.Handler) http.Handler {
if a.auth == nil {
return next
}
- return a.auth.RequireSession(next)
+ return a.auth.RequireSession(a.rejectDisabledHome(next))
}
diff --git a/internal/sheets/home.go b/internal/sheets/home.go
index 020a846..b9928a2 100644
--- a/internal/sheets/home.go
+++ b/internal/sheets/home.go
@@ -103,6 +103,13 @@ func (a *App) homePerson(ctx context.Context, q linkstore.Queryer, id int64) (st
func (a *App) homeRequire(next http.Handler) http.Handler {
return a.homeSessions.Middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session, ok := a.homeSessions.From(r)
+ if ok && session.Method == connect.HomeMethod {
+ if _, err := a.home.ResolveSession(r); errors.Is(err, linkstore.ErrStorage) {
+ w.Header().Set("Cache-Control", "no-store")
+ http.Error(w, "Sign-in unavailable.", http.StatusServiceUnavailable)
+ return
+ }
+ }
address, admitted := a.identity(r)
if !ok || !admitted || !a.Admits(address) {
if ok {
@@ -148,3 +155,14 @@ func (a *App) homeLocalBegin(w http.ResponseWriter, r *http.Request) {
a.render(w, r, "home_oauth", p.Title, http.StatusOK, p)
})(w, r)
}
+
+func (a *App) rejectDisabledHome(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if s, ok := sessions.Current(r); ok && s.Method == connect.HomeMethod {
+ w.Header().Set("Cache-Control", "no-store")
+ http.Error(w, "Sign in first.", http.StatusForbidden)
+ return
+ }
+ next.ServeHTTP(w, r)
+ })
+}
diff --git a/internal/sheets/home_navigation_test.go b/internal/sheets/home_navigation_test.go
index 782c1da..6998448 100644
--- a/internal/sheets/home_navigation_test.go
+++ b/internal/sheets/home_navigation_test.go
@@ -98,3 +98,53 @@ func TestHomeFrameworkPolicyAndDuplicateCookies(t *testing.T) {
t.Fatal("duplicate session accepted", w.Code)
}
}
+
+func TestHomeDisabledRefusesRetainedOpaqueSession(t *testing.T) {
+ f := newHomeFixture(t)
+ f.local(t, signedInAddress)
+ f.link(t, "retained-home")
+ f.jar = map[string]*http.Cookie{}
+ start := f.request(t, "GET", "/auth/start", nil, false)
+ if w := f.exchange(t, start, "retained-home"); w.Code != 303 {
+ t.Fatal(w.Code)
+ }
+ f.app.home = nil
+ f.app.homeRoutes = nil
+ f.app.homeSessions = nil
+ var before, after int64
+ f.app.db.Model(&Member{}).Count(&before)
+ w := f.request(t, "GET", "/sheets", nil, false)
+ if w.Code != 403 {
+ t.Fatal("retained Home session accepted", w.Code)
+ }
+ f.app.db.Model(&Member{}).Count(&after)
+ if after != before {
+ t.Fatal("opaque member created")
+ }
+}
+func TestHomeStorageFailureDoesNotRevokeSession(t *testing.T) {
+ f := newHomeFixture(t)
+ f.local(t, signedInAddress)
+ f.link(t, "storage-home")
+ f.jar = map[string]*http.Cookie{}
+ if w := f.exchange(t, f.request(t, "GET", "/auth/start", nil, false), "storage-home"); w.Code != 303 {
+ t.Fatal(w.Code)
+ }
+ cookie := f.jar[f.app.homeSessions.CookieName()]
+ if err := f.app.db.Exec("ALTER TABLE home_identity_links RENAME TO unavailable_links").Error; err != nil {
+ t.Fatal(err)
+ }
+ w := f.request(t, "GET", "/sheets", nil, false)
+ if w.Code != 503 {
+ t.Fatal("storage response", w.Code)
+ }
+ if f.jar[cookie.Name] == nil || f.jar[cookie.Name].Value != cookie.Value {
+ t.Fatal("storage failure revoked session")
+ }
+ if err := f.app.db.Exec("ALTER TABLE unavailable_links RENAME TO home_identity_links").Error; err != nil {
+ t.Fatal(err)
+ }
+ if w := f.request(t, "GET", "/sheets", nil, false); w.Code != 200 {
+ t.Fatal("session did not recover", w.Code)
+ }
+}
diff --git a/internal/sheets/page.go b/internal/sheets/page.go
index 945549d..f546fc9 100644
--- a/internal/sheets/page.go
+++ b/internal/sheets/page.go
@@ -1,8 +1,9 @@
package sheets
import (
- "amadan.net/oficina/home/connect"
"net/http"
+
+ "amadan.net/oficina/home/connect"
)
// The root page is the anonymous entry page, and nothing else.
@@ -52,7 +53,7 @@ func (a *App) identity(r *http.Request) (string, bool) {
return "", false
}
id, ok := a.auth.SessionFrom(r)
- if !ok {
+ if !ok || string(id.Method) == connect.HomeMethod {
return "", false
}
return id.Address, true