Refactor group access, implement hidden/private/closed
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+76
-7
@@ -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
|
||||
|
||||
+5
-66
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user