Files
felhom.eu/hub/internal/api/crosscustomer_prefs_test.go
T

64 lines
2.8 KiB
Go

package api
import (
"net/http"
"testing"
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
)
// One box's key must not act for another household (found 2026-10-09 by a security review of the R-922 change):
// POST /preferences and POST /notify checked only that the key was valid, then trusted the body's customer_id — so
// box B could rewrite (since R-922: delete) household A's notification address, or make the hub mail household A any
// text. Every other customer-scoped controller route already refuses a mismatch (/event, /status, /offsite …).
// Red-proof: with the checkAuthCustomer mismatch test removed from either handler, its test below fails.
func newTwoCustomerHandler(t *testing.T) (*Handler, *store.Store) {
t.Helper()
h, st := newEventTestHandler(t) // c1 / ckey
if err := st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c2", APIKey: "c2key", RetrievalPassword: "p"}); err != nil {
t.Fatalf("SaveCustomerConfig c2: %v", err)
}
if err := st.SaveNotificationPrefs("c1", "seeded@example.hu", []string{"backup_failed"}, 6); err != nil {
t.Fatalf("stored prefs: %v", err)
}
return h, st
}
func TestSavePreferences_OtherHouseholdsKeyIsRefused(t *testing.T) {
h, st := newTwoCustomerHandler(t)
rr := do(h, http.MethodPost, "/preferences", "c2key",
`{"customer_id":"c1","email":"","enabled_events":[],"cooldown_hours":6,"email_cleared":true}`)
if rr.Code != http.StatusForbidden {
t.Errorf("c2's key acting for c1: status = %d, want 403", rr.Code)
}
prefs, _ := st.GetNotificationPrefs("c1")
if prefs == nil || prefs.Email != "seeded@example.hu" || len(prefs.EnabledEvents) != 1 {
t.Fatalf("another household's key changed c1's preferences: %+v", prefs)
}
}
func TestSavePreferences_OwnAndGlobalKeyStillWork(t *testing.T) {
h, st := newTwoCustomerHandler(t)
if rr := do(h, http.MethodPost, "/preferences", "ckey",
`{"customer_id":"c1","email":"own@example.hu","enabled_events":["node_down"],"cooldown_hours":6}`); rr.Code != http.StatusOK {
t.Fatalf("own key: status = %d", rr.Code)
}
if rr := do(h, http.MethodPost, "/preferences", globalKey,
`{"customer_id":"c1","email":"global@example.hu","enabled_events":["node_down"],"cooldown_hours":6}`); rr.Code != http.StatusOK {
t.Fatalf("global key: status = %d", rr.Code)
}
if prefs, _ := st.GetNotificationPrefs("c1"); prefs == nil || prefs.Email != "global@example.hu" {
t.Fatalf("own/global updates must apply: %+v", prefs)
}
}
func TestNotify_OtherHouseholdsKeyIsRefused(t *testing.T) {
h, _ := newTwoCustomerHandler(t)
rr := do(h, http.MethodPost, "/notify", "c2key",
`{"customer_id":"c1","event_type":"backup_failed","severity":"error","message":"spoofed"}`)
if rr.Code != http.StatusForbidden {
t.Fatalf("c2's key asking the hub to mail c1: status = %d, want 403 (body %s)", rr.Code, rr.Body.String())
}
}