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:
1 parent
5cf210ae97
commit
b4dcb74404
3 files changed
+88
-2
No files matched your search
@@ -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")
|
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"])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ const localSub = "local"
|
|||||||
// Version ist die einzige Stelle, an der sie steht. build.sh liest sie hier aus
|
// 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.
|
// 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.
|
// 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 {
|
type Config struct {
|
||||||
Addr string
|
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) {
|
func (s *Server) handleApps(w http.ResponseWriter, r *http.Request, sub string) {
|
||||||
writeJSON(w, http.StatusOK, map[string]any{
|
writeJSON(w, http.StatusOK, map[string]any{
|
||||||
"apps": s.store.Apps(),
|
"apps": s.store.Apps(),
|
||||||
"settings": s.store.User(sub).Settings,
|
"settings": s.store.SettingsFor(sub),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -208,6 +208,41 @@ func (s *Store) SetSettings(sub string, in map[string]string) error {
|
|||||||
return s.save()
|
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
|
// AppOrigins nennt die Ursprünge der eingetragenen Apps. Nur sie dürfen die
|
||||||
// Leiste mit Anmeldedaten abfragen — die Liste ist damit ihre eigene Freigabe.
|
// Leiste mit Anmeldedaten abfragen — die Liste ist damit ihre eigene Freigabe.
|
||||||
func (s *Store) AppOrigins() map[string]bool {
|
func (s *Store) AppOrigins() map[string]bool {
|
||||||
|
|||||||
Reference in new issue
Block a user