CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (windows-latest) (push) Canceled after 0s
CI / Storage tests on PostgreSQL (push) Canceled after 0s
CI / Complement (federation off) (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
CI / Smoke test the binary (push) Canceled after 0s
POST /keys/query for a remote user who shares a room with a local user is
now answered from a cache, fetched whole with GET /user/devices/{userId}
on a miss. Incoming m.device_list_update EDUs are applied when their
prev_id matches the cached stream_id; anything else marks the copy stale
and it is fetched again in the background, and a stale copy is never
served. m.signing_key_update replaces the cached cross-signing keys. A
user's copy is dropped once no local user shares a room with them, by a
keyserver listener on the roomserver's emit stage.
Migration 0009 adds keyserver_remote_users, keyserver_remote_device_keys
and keyserver_remote_cross_signing_keys for sqlite and postgres.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
101 lines
3.0 KiB
Go
101 lines
3.0 KiB
Go
package setup
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
|
|
"github.com/dariuszjaniak/warp/server/internal/federationapi"
|
|
"github.com/dariuszjaniak/warp/server/internal/keyserver"
|
|
"github.com/dariuszjaniak/warp/server/internal/matrix/event"
|
|
"github.com/dariuszjaniak/warp/server/internal/roomserver"
|
|
"github.com/dariuszjaniak/warp/server/internal/storage"
|
|
)
|
|
|
|
// fanout is the roomserver's emit stage when more than one thing listens:
|
|
// the sync notifier, which wakes long polls, and the federation queue,
|
|
// which sends events to other servers.
|
|
//
|
|
// Each listener is held to the roomserver's rule for a Notifier — never
|
|
// block — so calling them in turn under the room lock is fine.
|
|
type fanout struct {
|
|
mu sync.RWMutex
|
|
listeners []roomserver.Notifier
|
|
}
|
|
|
|
func (f *fanout) add(n roomserver.Notifier) {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
f.listeners = append(f.listeners, n)
|
|
}
|
|
|
|
// EventPersisted implements roomserver.Notifier.
|
|
func (f *fanout) EventPersisted(ctx context.Context, n roomserver.Notification) {
|
|
f.mu.RLock()
|
|
defer f.mu.RUnlock()
|
|
for _, l := range f.listeners {
|
|
l.EventPersisted(ctx, n)
|
|
}
|
|
}
|
|
|
|
// keyMemberships is the keyserver's listener on the emit stage: a
|
|
// membership ending is when a remote user may stop sharing a room with
|
|
// anybody here, and the keyserver drops its cached copy of their keys once
|
|
// they have. MembershipLeft does not block, as the emit stage requires.
|
|
type keyMemberships struct{ keys *keyserver.API }
|
|
|
|
// EventPersisted implements roomserver.Notifier.
|
|
func (l keyMemberships) EventPersisted(_ context.Context, n roomserver.Notification) {
|
|
if n.Event == nil || n.Event.Type() != event.TypeMember || !n.StateChanged {
|
|
return
|
|
}
|
|
userID, ok := n.Event.StateKey()
|
|
if !ok {
|
|
return
|
|
}
|
|
switch membership, _ := n.Event.Membership(); membership {
|
|
case event.MembershipLeave, event.MembershipBan:
|
|
l.keys.MembershipLeft(userID)
|
|
}
|
|
}
|
|
|
|
// federationStatus adapts the module to the admin API.
|
|
type federationStatus struct {
|
|
module *federationapi.Module
|
|
}
|
|
|
|
func (s federationStatus) Running() bool { return s.module.Running() }
|
|
|
|
func (s federationStatus) Destinations(ctx context.Context) ([]storage.DestinationSummary, error) {
|
|
return s.module.Queue().Destinations(ctx)
|
|
}
|
|
|
|
func (s federationStatus) ResetBackoff(ctx context.Context, serverName string) error {
|
|
return s.module.Queue().ResetBackoff(ctx, serverName)
|
|
}
|
|
|
|
// federationTLS builds the client TLS configuration for outbound
|
|
// federation: the system roots, plus a configured CA bundle.
|
|
func federationTLS(caFile string) (*tls.Config, error) {
|
|
cfg := &tls.Config{MinVersion: tls.VersionTLS12}
|
|
if caFile == "" {
|
|
return cfg, nil
|
|
}
|
|
pool, err := x509.SystemCertPool()
|
|
if err != nil || pool == nil {
|
|
pool = x509.NewCertPool()
|
|
}
|
|
pem, err := os.ReadFile(caFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read federation.tls_ca_file: %w", err)
|
|
}
|
|
if !pool.AppendCertsFromPEM(pem) {
|
|
return nil, fmt.Errorf("federation.tls_ca_file %s contains no PEM certificates", caFile)
|
|
}
|
|
cfg.RootCAs = pool
|
|
return cfg, nil
|
|
}
|