package flare_test import ( "context" "crypto/ecdh" "crypto/rand" "encoding/base64" "errors" "fmt" "net/http" "net/http/httptest" "strings" "time" "git.golem15.com/golem15/summercms/modules/flare" ) // browserSubscription stands in for the PushSubscription a browser posts to // the application: an endpoint and the subscriber's p256dh and auth keys. func browserSubscription(endpoint string) (flare.Subscription, error) { key, err := ecdh.P256().GenerateKey(rand.Reader) if err != nil { return flare.Subscription{}, err } auth := make([]byte, 16) if _, err := rand.Read(auth); err != nil { return flare.Subscription{}, err } enc := base64.RawURLEncoding return flare.Subscription{ Endpoint: endpoint, P256dh: enc.EncodeToString(key.PublicKey().Bytes()), Auth: enc.EncodeToString(auth), }, nil } func ExampleVAPIDPusher_Send() { // A stand-in push service: 201 for a live subscription, 410 for one the // browser dropped. push := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/gone" { w.WriteHeader(http.StatusGone) return } fmt.Println("push service got", r.Header.Get("Content-Encoding"), r.Header.Get("TTL"), r.Header.Get("Urgency")) w.WriteHeader(http.StatusCreated) })) defer push.Close() keys, err := flare.GenerateVAPIDKeys() // websockets:generate-vapid-keys if err != nil { fmt.Println(err) return } cfg := flare.Config{ Enabled: true, PublicKey: keys.PublicKey, PrivateKey: keys.PrivateKey, Subject: "mailto:admin@example.com", TTL: time.Hour, AllowedHosts: []string{"127.0.0.1"}, // production keeps the default push services } // The test server's client trusts its certificate. pusher := flare.NewVAPIDPusher(cfg, push.Client()) ctx := context.Background() payload := []byte(`{"title":"New comment","body":"Someone replied to your post"}`) for _, endpoint := range []string{ push.URL + "/live", push.URL + "/gone", "https://push.attacker.example/steal", "http://127.0.0.1/plain", } { sub, err := browserSubscription(endpoint) if err != nil { fmt.Println(err) return } err = pusher.Send(ctx, sub, payload, flare.SendOptions{Urgency: "normal"}) switch { case err == nil: fmt.Println("sent") case errors.Is(err, flare.ErrSubscriptionGone): fmt.Println("gone: delete the subscription") case errors.Is(err, flare.ErrEndpointNotAllowed): fmt.Println("refused before connecting") default: fmt.Println("error:", err) } } // Formatting the keys never prints the private key. fmt.Println(strings.Contains(fmt.Sprintf("%v %#v", keys, keys), keys.PrivateKey)) // Output: // push service got aes128gcm 3600 normal // sent // gone: delete the subscription // refused before connecting // refused before connecting // false } func ExampleHostAllowed() { allowed := []string{"fcm.googleapis.com", "*.push.apple.com"} for _, host := range []string{"fcm.googleapis.com", "api.push.apple.com", "push.apple.com", "evil.example"} { fmt.Println(host, flare.HostAllowed(host, allowed)) } // Output: // fcm.googleapis.com true // api.push.apple.com true // push.apple.com false // evil.example false }