when in "none" ACL mode, privileged checks are not enforced
This commit is contained in:
@@ -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,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v0.27.8
|
v0.27.9
|
||||||
Reference in New Issue
Block a user