fix: das Aussehen erreicht die Leiste auch über die Site-Grenze

Marcs Frage: gilt die Einstellung vom Rechner auch für die Leiste am Handy?

Sie hätte es nicht getan, sobald HONEYOS_USER_HEADER gesetzt ist: die
Einstellungen liegen dann unter "proxy:marc", die Leiste fragt aber ohne
Sitzung und landet beim Standardnutzer — dessen Satz ist leer, die Leiste
wäre bernsteinfarben geblieben, während der Schreibtisch grün leuchtet.

SettingsFor() hilft nach: gibt es genau einen Nutzer mit Einstellungen,
gelten dessen. Bei mehreren bleibt es beim Standard, dann ist nicht zu
erraten, wer da schaut.

Nachgestellt mit einem Pixel-7-Profil ohne jedes Cookie: die Leiste kommt
grün an.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-24 22:47:19 +02:00
1 parent 5cf210ae97
commit b4dcb74404
3 files changed
+88 -2

No files matched your search

+51
View File
@@ -595,3 +595,54 @@ func TestVersionKommtVomServer(t *testing.T) {
t.Error("os.js trägt wieder eine eigene Version — die läuft mit den Image-Tags auseinander")
}
}
// Der Fall, nach dem Marc gefragt hat: Einstellungen am Rechner setzen, und die
// Leiste am Handy trägt dieselbe Farbe. Sie fragt über die Site-Grenze, landet
// also beim Standardnutzer — die Einstellungen können aber unter einem Namen
// aus der Proxy-Kopfzeile liegen.
func TestLeisteFindetDasAussehenAuchÜberDieSiteGrenze(t *testing.T) {
s := proxyServer(t, Config{ProxyAuth: true, UserHeader: "Remote-User", AppsKey: "geheim123"})
h := s.routes()
// Am Rechner, durch den Proxy: grün.
req := httptest.NewRequest("PUT", "/api/settings", strings.NewReader(`{"phosphor":"green"}`))
req.Header.Set("Remote-User", "marc")
rec := httptest.NewRecorder()
h.ServeHTTP(rec, req)
if rec.Code != http.StatusNoContent {
t.Fatalf("speichern: %d", rec.Code)
}
// Am Handy fragt die Leiste nur mit dem Schlüssel — ohne Namen.
req = httptest.NewRequest("GET", "/api/apps", nil)
req.Header.Set("X-HoneyOS-Key", "geheim123")
rec = httptest.NewRecorder()
h.ServeHTTP(rec, req)
var got struct{ Settings map[string]string }
_ = json.Unmarshal(rec.Body.Bytes(), &got)
if got.Settings["phosphor"] != "green" {
t.Fatalf("die Leiste bekäme %q statt grün — auf dem Handy bliebe sie bernstein", got.Settings["phosphor"])
}
}
// Bei mehreren Nutzern darf nicht geraten werden, wessen Farbe gilt.
func TestBeiMehrerenNutzernWirdNichtGeraten(t *testing.T) {
s := proxyServer(t, Config{ProxyAuth: true, UserHeader: "Remote-User", AppsKey: "geheim123"})
for name, farbe := range map[string]string{"marc": "green", "gast": "ice"} {
if err := s.store.SetSettings("proxy:"+name, map[string]string{"phosphor": farbe}); err != nil {
t.Fatal(err)
}
}
req := httptest.NewRequest("GET", "/api/apps", nil)
req.Header.Set("X-HoneyOS-Key", "geheim123")
rec := httptest.NewRecorder()
s.routes().ServeHTTP(rec, req)
var got struct{ Settings map[string]string }
_ = json.Unmarshal(rec.Body.Bytes(), &got)
if got.Settings["phosphor"] != "" {
t.Errorf("es wurde geraten: %q", got.Settings["phosphor"])
}
}
+2 -2
View File
@@ -27,7 +27,7 @@ const localSub = "local"
// Version ist die einzige Stelle, an der sie steht. build.sh liest sie hier aus
// und vergibt denselben Docker-Tag; die Oberfläche holt sie über /api/public.
// Vorher stand sie zusätzlich in os.js und lief mit den Image-Tags auseinander.
const Version = "0.5"
const Version = "0.6"
type Config struct {
Addr string
@@ -335,7 +335,7 @@ func (s *Server) handleSetSettings(w http.ResponseWriter, r *http.Request, sub s
func (s *Server) handleApps(w http.ResponseWriter, r *http.Request, sub string) {
writeJSON(w, http.StatusOK, map[string]any{
"apps": s.store.Apps(),
"settings": s.store.User(sub).Settings,
"settings": s.store.SettingsFor(sub),
})
}
+35
View File
@@ -208,6 +208,41 @@ func (s *Store) SetSettings(sub string, in map[string]string) error {
return s.save()
}
// SettingsFor liefert das Aussehen für sub — und hilft nach, wenn die Frage
// über eine Site-Grenze kam.
//
// Die Leiste in einer fremden App weist sich nur mit dem Schlüssel aus, nicht
// mit einer Sitzung: der Browser schickt das Cookie dorthin nicht mit. Sie
// landet deshalb beim Standardnutzer. Sind die Einstellungen aber unter einem
// Namen aus der Proxy-Kopfzeile gespeichert, wäre der leer — und die Leiste
// bliebe bernsteinfarben, während der Schreibtisch grün leuchtet.
//
// Gibt es genau einen Nutzer mit Einstellungen, gilt dessen Aussehen. Bei
// mehreren bleibt es beim Standard: dann ist nicht zu erraten, wer da schaut.
func (s *Store) SettingsFor(sub string) map[string]string {
if own := s.User(sub).Settings; len(own) > 0 {
return own
}
s.mu.RLock()
defer s.mu.RUnlock()
var einziger map[string]string
for _, u := range s.d.Users {
if len(u.Settings) == 0 {
continue
}
if einziger != nil {
return map[string]string{} // mehrere — kein Rateweg
}
einziger = u.Settings
}
out := map[string]string{}
for k, v := range einziger {
out[k] = v
}
return out
}
// AppOrigins nennt die Ursprünge der eingetragenen Apps. Nur sie dürfen die
// Leiste mit Anmeldedaten abfragen — die Liste ist damit ihre eigene Freigabe.
func (s *Store) AppOrigins() map[string]bool {