From 7497dacf8d1bd2156f141d3c7f5252bf1019a5ad Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Fri, 31 Oct 2025 10:59:24 -0700 Subject: [PATCH] Refactor group access, implement hidden/private/closed --- README.md | 2 -- zooid/config.go | 1 - zooid/groups.go | 83 +++++++++++++++++++++++++++++++++++++++++++---- zooid/instance.go | 71 +++------------------------------------- 4 files changed, 81 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 68d6ce8..405fd00 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,6 @@ Configures NIP 29 support. - `enabled` - whether NIP 29 is enabled. - `auto_join` - whether relay members can join `open` groups without approval. Defaults to `true`. -- `auto_leave` - whether relay members can leave groups without approval. Defaults to `true`. ### `[management]` @@ -90,7 +89,6 @@ strip_signatures = false [groups] enabled = true auto_join = false -auto_leave = true [management] enabled = true diff --git a/zooid/config.go b/zooid/config.go index 764d866..a7369ca 100644 --- a/zooid/config.go +++ b/zooid/config.go @@ -33,7 +33,6 @@ type Config struct { Groups struct { Enabled bool `toml:"enabled"` AutoJoin bool `toml:"auto_join"` - AutoLeave bool `toml:"auto_leave"` } `toml:"groups"` Management struct { diff --git a/zooid/groups.go b/zooid/groups.go index e2093fa..1a0ae9e 100644 --- a/zooid/groups.go +++ b/zooid/groups.go @@ -223,25 +223,94 @@ func (g *GroupStore) UpdateMembersList(h string) error { // Other stuff func (g *GroupStore) HasAccess(h string, pubkey nostr.PubKey) bool { + return g.IsAdmin(h, pubkey) || g.IsMember(h, pubkey) +} + +func (g *GroupStore) IsGroupEvent(event nostr.Event) bool { + if slices.Contains(nip29.MetadataEventKinds, event.Kind) { + return true + } + + if slices.Contains(nip29.ModerationEventKinds, event.Kind) { + return true + } + + joinKinds := []nostr.Kind{ + nostr.KindSimpleGroupJoinRequest, + nostr.KindSimpleGroupLeaveRequest, + } + + if slices.Contains(joinKinds, event.Kind) { + return true + } + + return GetGroupIDFromEvent(event) != "" +} + +func (g *GroupStore) CanRead(pubkey nostr.PubKey, event nostr.Event) bool { + if !g.Config.Groups.Enabled { + return false + } + + h := GetGroupIDFromEvent(event) meta, found := g.GetMetadata(h) if !found { return false } - if !HasTag(meta.Tags, "closed") { - return true + if HasTag(meta.Tags, "hidden") && !g.HasAccess(h, pubkey) { + return false } - if g.IsAdmin(h, pubkey) { - return true + if HasTag(meta.Tags, "private") && !g.HasAccess(h, pubkey) { + return false } - if g.IsMember(h, pubkey) { - return true + return true +} + +func (g *GroupStore) CheckWrite(event nostr.Event) string { + if !g.Config.Groups.Enabled { + return "invalid: groups are not enabled" } - return false + if slices.Contains(nip29.MetadataEventKinds, event.Kind) { + return "invalid: group metadata cannot be set directly" + } + + h := GetGroupIDFromEvent(event) + meta, found := g.GetMetadata(h) + + if event.Kind == nostr.KindSimpleGroupCreateGroup { + if found { + return "invalid: that group already exists" + } + } else if !found { + return "invalid: group not found" + } + + if event.Kind == nostr.KindSimpleGroupJoinRequest && g.IsMember(h, event.PubKey) { + return "duplicate: already a member" + } + + if event.Kind == nostr.KindSimpleGroupLeaveRequest && !g.IsMember(h, event.PubKey) { + return "duplicate: not currently a member" + } + + if slices.Contains(nip29.ModerationEventKinds, event.Kind) && !g.Config.CanManage(event.PubKey) { + return "restricted: you are not authorized to manage groups" + } + + if HasTag(meta.Tags, "hidden") && !g.HasAccess(h, event.PubKey) { + return "invalid: group not found" + } + + if HasTag(meta.Tags, "closed") && !g.HasAccess(h, event.PubKey) { + return "restricted: you are not a member of that group" + } + + return "" } // Middleware diff --git a/zooid/instance.go b/zooid/instance.go index 27abab1..a875c77 100644 --- a/zooid/instance.go +++ b/zooid/instance.go @@ -10,7 +10,6 @@ import ( "fiatjaf.com/nostr" "fiatjaf.com/nostr/khatru" - "fiatjaf.com/nostr/nip29" "github.com/gosimple/slug" ) @@ -317,19 +316,7 @@ func (instance *Instance) QueryStored(ctx context.Context, filter nostr.Filter) continue } - h := GetGroupIDFromEvent(event) - - if h != "" { - if !instance.Config.Groups.Enabled { - continue - } - - if !instance.Groups.HasAccess(h, pubkey) { - continue - } - } - - if !instance.Config.Groups.Enabled && slices.Contains(nip29.MetadataEventKinds, event.Kind) { + if instance.Groups.IsGroupEvent(event) && !instance.Groups.CanRead(pubkey, event) { continue } @@ -372,57 +359,9 @@ func (instance *Instance) OnEvent(ctx context.Context, event nostr.Event) (rejec return true, "invalid: this event's kind is not accepted" } - if slices.Contains(nip29.MetadataEventKinds, event.Kind) { - return true, "invalid: group metadata cannot be set directly" - } - - if slices.Contains(nip29.ModerationEventKinds, event.Kind) && !instance.Config.CanManage(event.PubKey) { - return true, "restricted: you are not authorized to manage groups" - } - - allGroupKinds := append( - nip29.ModerationEventKinds, - nostr.KindSimpleGroupJoinRequest, - nostr.KindSimpleGroupLeaveRequest, - ) - - h := GetGroupIDFromEvent(event) - - if slices.Contains(allGroupKinds, event.Kind) { - if !instance.Config.Groups.Enabled { - return true, "invalid: group events not accepted on this relay" - } - - if h == "" { - return true, "invalid: h tag is required" - } - - _, found := instance.Groups.GetMetadata(h) - - if event.Kind == nostr.KindSimpleGroupCreateGroup { - if found { - return true, "invalid: that group already exists" - } - } else if !found { - return true, "invalid: no such group exists" - } - - if event.Kind == nostr.KindSimpleGroupJoinRequest && instance.Groups.IsMember(h, event.PubKey) { - return true, "duplicate: already a member" - } - - if event.Kind == nostr.KindSimpleGroupLeaveRequest && !instance.Groups.IsMember(h, event.PubKey) { - return true, "duplicate: not currently a member" - } - } else if h != "" { - _, found := instance.Groups.GetMetadata(h) - - if !found { - return true, "invalid: no such group exists" - } - - if !instance.Groups.HasAccess(h, pubkey) { - return true, "restricted: you are not a member of that group" + if instance.Groups.IsGroupEvent(event) { + if err := instance.Groups.CheckWrite(event); err != "" { + return true, err } } @@ -455,7 +394,7 @@ func (instance *Instance) OnEventSaved(ctx context.Context, event nostr.Event) { } } - if event.Kind == nostr.KindSimpleGroupLeaveRequest && instance.Config.Groups.AutoLeave { + if event.Kind == nostr.KindSimpleGroupLeaveRequest { instance.Groups.RemoveMember(h, event.PubKey) instance.Groups.UpdateMembersList(h) }