Erst die Sitzung, dann der Schluessel

Der Schluessel kennt keinen Nutzer und landete beim Standardnutzer — auch
dann, wenn die App unter derselben Site liegt und der Browser das
HoneyOS-Cookie laengst mitschickt (cloud.honeyos.… und honeyos.… sind
dieselbe Site, duckdns.org steht auf der Public-Suffix-Liste). Damit trug
die HoneyBar in Aether die Farben eines fremden Nutzers.

Jetzt sieht /api/apps erst nach der Sitzung, und nur bei eigener
Anmeldung: Im offenen Betrieb und hinter einem Anmelde-Proxy gilt jede
Anfrage als angemeldet, dort bleibt der Schluessel das letzte Wort.
Test dazu, mit zwei Nutzern, damit der Rueckfall auf den einzigen Nutzer
das Ergebnis nicht zufaellig richtig aussehen laesst.
This commit is contained in:
marc committed 2026-08-29 23:29:53 +02:00
1 parent ffaba616ce
commit cc7a50c621
4 files changed
+111 -14

No files matched your search

+63
View File
@@ -483,6 +483,69 @@ func TestAppListeBrauchtDenSchlüssel(t *testing.T) {
}
}
// Liegt die App unter derselben Site, kommt die Sitzung mit — dann muss die
// HoneyBar die Einstellungen des Angemeldeten bekommen und nicht die des
// Standardnutzers. Der Schlüssel darf dabei ruhig mitgeschickt werden: Er ist
// die Krücke für den fremden Fall und darf die Sitzung nicht überstimmen.
func TestSitzungSchlaegtSchluessel(t *testing.T) {
idp := newFakeIdP(t, "honeyos")
s := newTestServer(t, idp.URL)
s.cfg.AppsKey = "geheim123"
s.auth = auth.New(s.cfg)
h := s.routes()
sess, csrf := signIn(t, h)
// Der Angemeldete stellt seine Röhre auf grün.
req := httptest.NewRequest("PUT", "/api/settings", strings.NewReader(`{"phosphor":"green"}`))
req.Header.Set("X-CSRF-Token", csrf.Value)
req.AddCookie(sess)
req.AddCookie(csrf)
rec := httptest.NewRecorder()
h.ServeHTTP(rec, req)
if rec.Code != http.StatusNoContent {
t.Fatalf("Einstellungen speichern: %d", rec.Code)
}
// Ein zweiter Nutzer mit anderer Röhre — sonst könnte der Rückfall auf den
// „einzigen Nutzer" das Ergebnis zufällig richtig aussehen lassen.
if err := s.store.SetSettings("jemand-anders", map[string]string{"phosphor": "ice"}); err != nil {
t.Fatal(err)
}
hole := func(mod func(*http.Request)) map[string]any {
req := httptest.NewRequest("GET", "/api/apps", nil)
mod(req)
rec := httptest.NewRecorder()
h.ServeHTTP(rec, req)
if rec.Code != 200 {
t.Fatalf("/api/apps: %d", rec.Code)
}
var out map[string]any
if err := json.Unmarshal(rec.Body.Bytes(), &out); err != nil {
t.Fatal(err)
}
return out
}
mitBeidem := hole(func(r *http.Request) {
r.AddCookie(sess)
r.Header.Set("X-HoneyOS-Key", "geheim123")
})
if got := mitBeidem["settings"].(map[string]any)["phosphor"]; got != "green" {
t.Errorf("mit Sitzung und Schlüssel: %v, erwartet green (die des Angemeldeten)", got)
}
// Ohne Sitzung bleibt der Schlüssel der Weg — und landet beim
// Standardnutzer. Bei zwei Nutzern heißt das: kein Rateweg, leere Menge.
nurSchluessel := hole(func(r *http.Request) {
r.Header.Set("X-HoneyOS-Key", "geheim123")
})
if got := nurSchluessel["settings"].(map[string]any)["phosphor"]; got == "green" {
t.Errorf("nur mit Schlüssel: %v — der Schlüssel kennt keinen Nutzer und darf keinen raten", got)
}
}
// Der Schlüssel öffnet nur das Lesen der Liste, nicht das Schreiben.
func TestSchlüsselDarfNichtSchreiben(t *testing.T) {
idp := newFakeIdP(t, "honeyos")