package main import ( "fmt" "net/http" "strings" "testing" ) // A push subscription may only be removed by its own token — not by anyone who // happens to know the endpoint. func TestUnsubscribePushIsOwnerScoped(t *testing.T) { ts, srv := newTestServer(t) aTok := mintDeviceToken(t, srv.db, "A") bTok := mintDeviceToken(t, srv.db, "B") if err := srv.db.addSubscription(PushSub{Endpoint: "https://e/1", Token: aTok, P256dh: "p", Auth: "a"}); err != nil { t.Fatalf("addSubscription: %v", err) } post := func(body string) int { resp, err := http.Post(ts.URL+"/api/push/unsubscribe", "application/json", strings.NewReader(body)) if err != nil { t.Fatal(err) } resp.Body.Close() return resp.StatusCode } subCount := func() int { subs, _ := srv.db.listSubscriptions(); return len(subs) } // No token → refused, nothing deleted. if got := post(`{"endpoint":"https://e/1"}`); got != http.StatusForbidden { t.Fatalf("no token: want 403, got %d", got) } if subCount() != 1 { t.Fatalf("no-token request deleted a sub") } // Valid but non-owning token → accepted, but the other user's sub survives. if got := post(fmt.Sprintf(`{"token":%q,"endpoint":"https://e/1"}`, bTok)); got != http.StatusOK { t.Fatalf("non-owner: want 200, got %d", got) } if subCount() != 1 { t.Fatalf("non-owner deleted someone else's subscription") } // Owner token → removed. if got := post(fmt.Sprintf(`{"token":%q,"endpoint":"https://e/1"}`, aTok)); got != http.StatusOK { t.Fatalf("owner: want 200, got %d", got) } if subCount() != 0 { t.Fatalf("owner unsubscribe left the sub") } } // A second grant must not overwrite a member's existing wrapped instance key. func TestGrantInstanceKeyNeverOverwrites(t *testing.T) { db := newTestDB(t) if err := db.grantInstanceKey("u1", "GOOD"); err != nil { t.Fatalf("grant: %v", err) } if err := db.grantInstanceKey("u1", "JUNK"); err != nil { t.Fatalf("re-grant: %v", err) } got, _, _ := db.instanceKeyFor("u1") if got != "GOOD" { t.Fatalf("instance key overwritten: got %q, want GOOD", got) } } // Re-adding an existing member must not clobber their wrapped thread key. func TestAddThreadMemberNeverClobbers(t *testing.T) { db := newTestDB(t) if err := db.addThreadMember("t1", "m1", "GOOD"); err != nil { t.Fatalf("add: %v", err) } if err := db.addThreadMember("t1", "m1", "JUNK"); err != nil { t.Fatalf("re-add: %v", err) } var wrapped string if err := db.sql.QueryRow( "SELECT wrapped FROM thread_keys WHERE thread_id = ? AND user_id = ?", "t1", "m1", ).Scan(&wrapped); err != nil { t.Fatalf("read thread key: %v", err) } if wrapped != "GOOD" { t.Fatalf("thread key clobbered: got %q, want GOOD", wrapped) } }