- docs/backend: admin controllers, forms, lists and filters, relation manager, users and permissions, settings, partials and widgets, admin SPA - docs/services: storage, outbound HTTP, realtime, Web Push, search, parity testing and the Frontend and AJAX (not provided) page - Examples for cabana (with testdata/docs YAML), fetchguard, lighthouse and its centrifugo driver, flare, beachcomber and typesense, tide; lighthouse and beachcomber TestDocs* regions run on their Postgres harnesses - concept map rows link their guide pages and the not-provided rows the Frontend and AJAX page; index lists Backend, Database and Services - TestDocsRequiredPages asserts the D-08 section order
113 lines
3.2 KiB
Go
113 lines
3.2 KiB
Go
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
|
|
}
|