when in "none" ACL mode, privileged checks are not enforced
Some checks failed
Go / build (push) Has been cancelled
Go / release (push) Has been cancelled

This commit is contained in:
2025-11-13 08:31:02 +00:00
parent baede6d37f
commit 7169a2158f
3 changed files with 60 additions and 24 deletions

View File

@@ -661,6 +661,8 @@ func (l *Listener) HandleReq(msg []byte) (err error) {
l.subscriptionsMu.Unlock() l.subscriptionsMu.Unlock()
// Register subscription with publisher // Register subscription with publisher
// Set AuthRequired based on ACL mode - when ACL is "none", don't require auth for privileged events
authRequired := acl.Registry.Active.Load() != "none"
l.publishers.Receive( l.publishers.Receive(
&W{ &W{
Conn: l.conn, Conn: l.conn,
@@ -669,6 +671,7 @@ func (l *Listener) HandleReq(msg []byte) (err error) {
Receiver: receiver, Receiver: receiver,
Filters: &subbedFilters, Filters: &subbedFilters,
AuthedPubkey: l.authedPubkey.Load(), AuthedPubkey: l.authedPubkey.Load(),
AuthRequired: authRequired,
}, },
) )

View File

@@ -28,6 +28,7 @@ type Subscription struct {
remote string remote string
AuthedPubkey []byte AuthedPubkey []byte
Receiver event.C // Channel for delivering events to this subscription Receiver event.C // Channel for delivering events to this subscription
AuthRequired bool // Whether ACL requires authentication for privileged events
*filter.S *filter.S
} }
@@ -58,6 +59,11 @@ type W struct {
// AuthedPubkey is the authenticated pubkey associated with the listener (if any). // AuthedPubkey is the authenticated pubkey associated with the listener (if any).
AuthedPubkey []byte AuthedPubkey []byte
// AuthRequired indicates whether the ACL in operation requires auth. If
// this is set to true, the publisher will not publish privileged or other
// restricted events to non-authed listeners, otherwise, it will.
AuthRequired bool
} }
func (w *W) Type() (typeName string) { return Type } func (w *W) Type() (typeName string) { return Type }
@@ -87,7 +93,6 @@ func NewPublisher(c context.Context) (publisher *P) {
func (p *P) Type() (typeName string) { return Type } func (p *P) Type() (typeName string) { return Type }
// Receive handles incoming messages to manage websocket listener subscriptions // Receive handles incoming messages to manage websocket listener subscriptions
// and associated filters. // and associated filters.
// //
@@ -120,12 +125,14 @@ func (p *P) Receive(msg typer.T) {
if subs, ok := p.Map[m.Conn]; !ok { if subs, ok := p.Map[m.Conn]; !ok {
subs = make(map[string]Subscription) subs = make(map[string]Subscription)
subs[m.Id] = Subscription{ subs[m.Id] = Subscription{
S: m.Filters, remote: m.remote, AuthedPubkey: m.AuthedPubkey, Receiver: m.Receiver, S: m.Filters, remote: m.remote, AuthedPubkey: m.AuthedPubkey,
Receiver: m.Receiver, AuthRequired: m.AuthRequired,
} }
p.Map[m.Conn] = subs p.Map[m.Conn] = subs
} else { } else {
subs[m.Id] = Subscription{ subs[m.Id] = Subscription{
S: m.Filters, remote: m.remote, AuthedPubkey: m.AuthedPubkey, Receiver: m.Receiver, S: m.Filters, remote: m.remote, AuthedPubkey: m.AuthedPubkey,
Receiver: m.Receiver, AuthRequired: m.AuthRequired,
} }
} }
} }
@@ -174,11 +181,14 @@ func (p *P) Deliver(ev *event.E) {
for _, d := range deliveries { for _, d := range deliveries {
// If the event is privileged, enforce that the subscriber's authed pubkey matches // If the event is privileged, enforce that the subscriber's authed pubkey matches
// either the event pubkey or appears in any 'p' tag of the event. // either the event pubkey or appears in any 'p' tag of the event.
if kind.IsPrivileged(ev.Kind) { // Only check authentication if AuthRequired is true (ACL is active)
if kind.IsPrivileged(ev.Kind) && d.sub.AuthRequired {
if len(d.sub.AuthedPubkey) == 0 { if len(d.sub.AuthedPubkey) == 0 {
// Not authenticated - cannot see privileged events // Not authenticated - cannot see privileged events
log.D.F("subscription delivery DENIED for privileged event %s to %s (not authenticated)", log.D.F(
hex.Enc(ev.ID), d.sub.remote) "subscription delivery DENIED for privileged event %s to %s (not authenticated)",
hex.Enc(ev.ID), d.sub.remote,
)
continue continue
} }
@@ -201,8 +211,10 @@ func (p *P) Deliver(ev *event.E) {
} }
} }
if !allowed { if !allowed {
log.D.F("subscription delivery DENIED for privileged event %s to %s (auth mismatch)", log.D.F(
hex.Enc(ev.ID), d.sub.remote) "subscription delivery DENIED for privileged event %s to %s (auth mismatch)",
hex.Enc(ev.ID), d.sub.remote,
)
// Skip delivery for this subscriber // Skip delivery for this subscriber
continue continue
} }
@@ -225,26 +237,37 @@ func (p *P) Deliver(ev *event.E) {
} }
if hasPrivateTag { if hasPrivateTag {
canSeePrivate := p.canSeePrivateEvent(d.sub.AuthedPubkey, privatePubkey, d.sub.remote) canSeePrivate := p.canSeePrivateEvent(
d.sub.AuthedPubkey, privatePubkey, d.sub.remote,
)
if !canSeePrivate { if !canSeePrivate {
log.D.F("subscription delivery DENIED for private event %s to %s (unauthorized)", log.D.F(
hex.Enc(ev.ID), d.sub.remote) "subscription delivery DENIED for private event %s to %s (unauthorized)",
hex.Enc(ev.ID), d.sub.remote,
)
continue continue
} }
log.D.F("subscription delivery ALLOWED for private event %s to %s (authorized)", log.D.F(
hex.Enc(ev.ID), d.sub.remote) "subscription delivery ALLOWED for private event %s to %s (authorized)",
hex.Enc(ev.ID), d.sub.remote,
)
} }
} }
// Send event to the subscription's receiver channel // Send event to the subscription's receiver channel
// The consumer goroutine (in handle-req.go) will read from this channel // The consumer goroutine (in handle-req.go) will read from this channel
// and forward it to the client via the write channel // and forward it to the client via the write channel
log.D.F("attempting delivery of event %s (kind=%d) to subscription %s @ %s", log.D.F(
hex.Enc(ev.ID), ev.Kind, d.id, d.sub.remote) "attempting delivery of event %s (kind=%d) to subscription %s @ %s",
hex.Enc(ev.ID), ev.Kind, d.id, d.sub.remote,
)
// Check if receiver channel exists // Check if receiver channel exists
if d.sub.Receiver == nil { if d.sub.Receiver == nil {
log.E.F("subscription %s has nil receiver channel for %s", d.id, d.sub.remote) log.E.F(
"subscription %s has nil receiver channel for %s", d.id,
d.sub.remote,
)
continue continue
} }
@@ -253,11 +276,15 @@ func (p *P) Deliver(ev *event.E) {
case <-p.c.Done(): case <-p.c.Done():
continue continue
case d.sub.Receiver <- ev: case d.sub.Receiver <- ev:
log.D.F("subscription delivery QUEUED: event=%s to=%s sub=%s", log.D.F(
hex.Enc(ev.ID), d.sub.remote, d.id) "subscription delivery QUEUED: event=%s to=%s sub=%s",
hex.Enc(ev.ID), d.sub.remote, d.id,
)
case <-time.After(DefaultWriteTimeout): case <-time.After(DefaultWriteTimeout):
log.E.F("subscription delivery TIMEOUT: event=%s to=%s sub=%s", log.E.F(
hex.Enc(ev.ID), d.sub.remote, d.id) "subscription delivery TIMEOUT: event=%s to=%s sub=%s",
hex.Enc(ev.ID), d.sub.remote, d.id,
)
// Receiver channel is full - subscription consumer is stuck or slow // Receiver channel is full - subscription consumer is stuck or slow
// The subscription should be removed by the cleanup logic // The subscription should be removed by the cleanup logic
} }
@@ -285,7 +312,9 @@ func (p *P) removeSubscriberId(ws *websocket.Conn, id string) {
// SetWriteChan stores the write channel for a websocket connection // SetWriteChan stores the write channel for a websocket connection
// If writeChan is nil, the entry is removed from the map // If writeChan is nil, the entry is removed from the map
func (p *P) SetWriteChan(conn *websocket.Conn, writeChan chan publish.WriteRequest) { func (p *P) SetWriteChan(
conn *websocket.Conn, writeChan chan publish.WriteRequest,
) {
p.Mx.Lock() p.Mx.Lock()
defer p.Mx.Unlock() defer p.Mx.Unlock()
if writeChan == nil { if writeChan == nil {
@@ -296,7 +325,9 @@ func (p *P) SetWriteChan(conn *websocket.Conn, writeChan chan publish.WriteReque
} }
// GetWriteChan returns the write channel for a websocket connection // GetWriteChan returns the write channel for a websocket connection
func (p *P) GetWriteChan(conn *websocket.Conn) (chan publish.WriteRequest, bool) { func (p *P) GetWriteChan(conn *websocket.Conn) (
chan publish.WriteRequest, bool,
) {
p.Mx.RLock() p.Mx.RLock()
defer p.Mx.RUnlock() defer p.Mx.RUnlock()
ch, ok := p.WriteChans[conn] ch, ok := p.WriteChans[conn]
@@ -313,7 +344,9 @@ func (p *P) removeSubscriber(ws *websocket.Conn) {
} }
// canSeePrivateEvent checks if the authenticated user can see an event with a private tag // canSeePrivateEvent checks if the authenticated user can see an event with a private tag
func (p *P) canSeePrivateEvent(authedPubkey, privatePubkey []byte, remote string) (canSee bool) { func (p *P) canSeePrivateEvent(
authedPubkey, privatePubkey []byte, remote string,
) (canSee bool) {
// If no authenticated user, deny access // If no authenticated user, deny access
if len(authedPubkey) == 0 { if len(authedPubkey) == 0 {
return false return false

View File

@@ -1 +1 @@
v0.27.8 v0.27.9