fix(0006e): call RefreshSession after membership changes in all clients (audit 0008 N4)
A secured bus freezes per-subject permissions at connect time, so a peer that creates or joins a room after connecting cannot pub/sub on it until it reconnects (RefreshSession). No client called it, so under enforce+ACL the demos failed closed — pushing the operator to disable the ACL (a security regression at the operator's discretion). Wire the membership-change contract into every client: - cmd/worker: RefreshSession after CreateRoom, before publishing. - cmd/chat (simple): RefreshSession after CreateRoom+Join, before Subscribe. - cmd/chat (encrypted demo): A refreshes after CreateRoom; B refreshes after the invite+join, both before pub/sub. - local_files/bridge (gateway): RefreshSession after CreateRoom+Join, before Subscribe. - mobile: new Session.RefreshSession wrapper + the contract documented for callers. Contract (documented on the wrappers): after ANY membership change, call RefreshSession BEFORE pub/sub on the new room (it drops active subs, so it must precede Subscribe). On an unsecured/dev bus it is a harmless reconnect. Test: - TestClientCreateRoomRefreshPublishFlow: end-to-end under enforce+ACL, a peer creates a room, refreshes, invites a second peer who joins+refreshes+subscribes, and the publish is received — no manual intervention, the ACL stays on. CGO_ENABLED=0 go build/vet/test green; govulncheck 0 reachable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,12 @@ func runSimple(natsURL, ctrlURL, roomSub, idFile, caFile string) {
|
||||
if err := c.Join(roomID); err != nil {
|
||||
log.Fatalf("join: %v", err)
|
||||
}
|
||||
// Membership-change contract (issue 0006e): refresh so the just-created room's
|
||||
// subject is subscribable under enforce+ACL (permissions are frozen at connect
|
||||
// time). Must run BEFORE Subscribe — RefreshSession drops active subscriptions.
|
||||
if err := c.RefreshSession(); err != nil {
|
||||
log.Fatalf("refresh session after create room: %v", err)
|
||||
}
|
||||
sub, err := c.Subscribe(roomID, func(f frame.Frame, plaintext []byte) {
|
||||
fmt.Printf("[%s] %s: %s\n", f.Subject, shortID(f.Sender), string(plaintext))
|
||||
})
|
||||
@@ -122,12 +128,21 @@ func runEncryptedDemo(natsURL, ctrlURL, caFile string) {
|
||||
must(err, "A create room")
|
||||
fmt.Printf(" room.test -> %s (E2E, persisted, signed)\n", roomID)
|
||||
|
||||
// Membership-change contract (issue 0006e): A only became a member of this room
|
||||
// after connecting, so refresh to gain its subject + per-room JetStream API
|
||||
// under enforce+ACL before publishing.
|
||||
must(a.RefreshSession(), "A refresh after create room")
|
||||
|
||||
// A invites B (seals K to B's X25519 key).
|
||||
must(a.Invite(roomID, b.Endpoint()), "A invite B")
|
||||
|
||||
// B joins (fetches + decrypts K).
|
||||
must(b.Join(roomID), "B join")
|
||||
|
||||
// B became a member via the invite above; refresh so B can subscribe to the
|
||||
// room's subject under enforce+ACL (before subscribing — refresh drops subs).
|
||||
must(b.RefreshSession(), "B refresh after join")
|
||||
|
||||
// B subscribes; capture received plaintexts.
|
||||
recv := make(chan string, 4)
|
||||
subB, err := b.Subscribe(roomID, func(f frame.Frame, plaintext []byte) {
|
||||
|
||||
@@ -47,6 +47,13 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatalf("create room: %v", err)
|
||||
}
|
||||
// Membership-change contract (issue 0006e): the bus freezes per-subject
|
||||
// permissions at connect time, and this room did not exist then. Refresh the
|
||||
// session so the new room's subject becomes publishable under enforce+ACL. On
|
||||
// an unsecured/dev bus this is a harmless reconnect.
|
||||
if err := c.RefreshSession(); err != nil {
|
||||
log.Fatalf("refresh session after create room: %v", err)
|
||||
}
|
||||
log.Printf("room %q -> %s (subject %s, cleartext)", *roomSub, roomID, *roomSub)
|
||||
|
||||
stop := make(chan os.Signal, 1)
|
||||
|
||||
Reference in New Issue
Block a user