2023-09-06 20:49:18 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2024-02-28 16:59:16 +01:00
|
|
|
"errors"
|
2023-10-28 21:40:54 -03:00
|
|
|
"fmt"
|
2023-09-06 20:49:18 +02:00
|
|
|
"os"
|
|
|
|
|
|
|
|
"github.com/nbd-wtf/go-nostr"
|
|
|
|
)
|
|
|
|
|
2023-10-29 13:45:46 -03:00
|
|
|
type Whitelist map[string]string // { [user_pubkey]: [invited_by] }
|
2023-09-06 20:49:18 +02:00
|
|
|
|
2023-10-29 13:45:46 -03:00
|
|
|
func addToWhitelist(pubkey string, inviter string) error {
|
2023-11-02 16:08:53 -03:00
|
|
|
if !isPublicKeyInWhitelist(inviter) {
|
|
|
|
return fmt.Errorf("pubkey %s doesn't have permission to invite", inviter)
|
2023-09-06 20:49:18 +02:00
|
|
|
}
|
2023-11-02 16:08:53 -03:00
|
|
|
|
2024-02-02 09:29:54 -03:00
|
|
|
if !nostr.IsValidPublicKey(pubkey) {
|
2023-11-02 16:08:53 -03:00
|
|
|
return fmt.Errorf("pubkey invalid: %s", pubkey)
|
|
|
|
}
|
|
|
|
|
|
|
|
if isPublicKeyInWhitelist(pubkey) {
|
|
|
|
return fmt.Errorf("pubkey already in whitelist: %s", pubkey)
|
|
|
|
}
|
|
|
|
|
|
|
|
whitelist[pubkey] = inviter
|
2023-10-29 13:45:46 -03:00
|
|
|
return saveWhitelist()
|
|
|
|
}
|
2023-10-11 00:19:33 +02:00
|
|
|
|
2023-10-29 13:45:46 -03:00
|
|
|
func isPublicKeyInWhitelist(pubkey string) bool {
|
|
|
|
_, ok := whitelist[pubkey]
|
|
|
|
return ok
|
|
|
|
}
|
2023-09-06 20:49:18 +02:00
|
|
|
|
2025-04-05 09:56:41 -03:00
|
|
|
func canInviteMore(pubkey string) bool {
|
|
|
|
if pubkey == "" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if pubkey == s.RelayPubkey {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if !isPublicKeyInWhitelist(pubkey) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2024-02-02 09:29:54 -03:00
|
|
|
count := 0
|
|
|
|
for _, inviter := range whitelist {
|
2025-04-05 09:56:41 -03:00
|
|
|
if inviter == pubkey {
|
2024-02-02 09:29:54 -03:00
|
|
|
count++
|
|
|
|
}
|
2025-04-05 09:56:41 -03:00
|
|
|
if count >= s.MaxInvitesPerPerson {
|
|
|
|
return false
|
2024-02-02 09:29:54 -03:00
|
|
|
}
|
|
|
|
}
|
2025-04-05 09:56:41 -03:00
|
|
|
return true
|
2024-02-02 09:29:54 -03:00
|
|
|
}
|
|
|
|
|
2023-11-02 21:08:57 -03:00
|
|
|
func isAncestorOf(ancestor string, target string) bool {
|
|
|
|
parent, ok := whitelist[target]
|
|
|
|
if !ok {
|
|
|
|
// parent is not in whitelist, this means this is a top-level user and can
|
|
|
|
// only be deleted by manually editing the users.json file
|
|
|
|
return false
|
2023-10-28 20:21:15 -03:00
|
|
|
}
|
2023-11-02 21:08:57 -03:00
|
|
|
|
|
|
|
if parent == ancestor {
|
|
|
|
// if the pubkey is the parent, that means it is an ancestor
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise we climb one degree up and test with the parent of the target
|
|
|
|
return isAncestorOf(ancestor, parent)
|
2023-10-28 20:21:15 -03:00
|
|
|
}
|
|
|
|
|
2023-10-29 13:45:46 -03:00
|
|
|
func removeFromWhitelist(target string, deleter string) error {
|
|
|
|
// check if this user is a descendant of the user who issued the delete command
|
|
|
|
if !isAncestorOf(deleter, target) {
|
|
|
|
return fmt.Errorf("insufficient permissions to delete this")
|
2023-10-28 21:40:54 -03:00
|
|
|
}
|
2023-10-28 20:21:15 -03:00
|
|
|
|
2023-10-29 13:45:46 -03:00
|
|
|
// if we got here that means we have permission to delete the target
|
|
|
|
delete(whitelist, target)
|
|
|
|
|
|
|
|
// delete all people who were invited by the target
|
|
|
|
removeDescendantsFromWhitelist(target)
|
|
|
|
|
2023-10-28 20:21:15 -03:00
|
|
|
return saveWhitelist()
|
|
|
|
}
|
|
|
|
|
2023-10-29 13:45:46 -03:00
|
|
|
func removeDescendantsFromWhitelist(ancestor string) {
|
|
|
|
for pubkey, inviter := range whitelist {
|
|
|
|
if inviter == ancestor {
|
|
|
|
delete(whitelist, pubkey)
|
|
|
|
removeDescendantsFromWhitelist(pubkey)
|
|
|
|
}
|
2023-09-06 20:49:18 +02:00
|
|
|
}
|
2023-10-29 13:45:46 -03:00
|
|
|
}
|
2023-10-11 00:19:33 +02:00
|
|
|
|
2023-10-29 13:45:46 -03:00
|
|
|
func loadWhitelist() error {
|
2024-02-12 18:40:52 +01:00
|
|
|
b, err := os.ReadFile(s.UserdataPath)
|
2023-09-06 20:49:18 +02:00
|
|
|
if err != nil {
|
2024-02-28 16:59:16 +01:00
|
|
|
// If the whitelist file does not exist, with RELAY_PUBKEY
|
|
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
|
|
whitelist[s.RelayPubkey] = ""
|
|
|
|
jsonBytes, err := json.Marshal(&whitelist)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := os.WriteFile(s.UserdataPath, jsonBytes, 0644); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
} else {
|
|
|
|
return err
|
|
|
|
}
|
2023-09-06 20:49:18 +02:00
|
|
|
}
|
2023-10-11 00:19:33 +02:00
|
|
|
|
2023-10-29 13:45:46 -03:00
|
|
|
if err := json.Unmarshal(b, &whitelist); err != nil {
|
2023-09-06 20:49:18 +02:00
|
|
|
return err
|
|
|
|
}
|
2023-10-11 00:19:33 +02:00
|
|
|
|
2023-09-06 20:49:18 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-10-11 00:19:33 +02:00
|
|
|
func saveWhitelist() error {
|
2023-09-06 20:49:18 +02:00
|
|
|
jsonBytes, err := json.Marshal(whitelist)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-10-11 00:19:33 +02:00
|
|
|
|
2024-02-12 18:40:52 +01:00
|
|
|
if err := os.WriteFile(s.UserdataPath, jsonBytes, 0644); err != nil {
|
2023-09-06 20:49:18 +02:00
|
|
|
return err
|
|
|
|
}
|
2023-10-11 00:19:33 +02:00
|
|
|
|
2023-09-06 20:49:18 +02:00
|
|
|
return nil
|
|
|
|
}
|