actually blocking writes from non-whitelisted people, fixes in eventstore and some other UI tweaks.

This commit is contained in:
fiatjaf 2023-11-02 16:08:53 -03:00
parent 6565b4dcf5
commit 19bc00da24
No known key found for this signature in database
GPG Key ID: BAD43C4BE5C1A3A1
7 changed files with 83 additions and 48 deletions

View File

@ -32,16 +32,20 @@ func userRowComponent(ctx context.Context, profile sdk.ProfileMetadata, loggedUs
}
return Li(
A().Href("nostr:"+profile.Npub()).Children(
Span(profile.ShortName()).Attr(
"npub", profile.Npub(),
"name", profile.ShortName(),
"_", `
on mouseenter set my innerText to @npub then hide the next <button />
on mouseleave set my innerText to @name then show the next <button />`,
),
).Class("font-mono py-1"),
userNameComponent(ctx, profile),
button,
inviteTreeComponent(ctx, profile.PubKey, loggedUser),
).Class("ml-6")
}
func userNameComponent(ctx context.Context, profile sdk.ProfileMetadata) HTMLComponent {
return A().Href("nostr:" + profile.Npub()).Children(
Span(profile.ShortName()).Attr(
"npub", profile.Npub(),
"name", profile.ShortName(),
"_", `
on mouseenter set my innerText to @npub then hide the next <button />
on mouseleave set my innerText to @name then show the next <button />`,
),
).Class("font-mono py-1")
}

2
go.mod
View File

@ -3,7 +3,7 @@ module github.com/github-tijlxyz/khatru-invite
go 1.21.0
require (
github.com/fiatjaf/eventstore v0.0.2
github.com/fiatjaf/eventstore v0.1.0
github.com/fiatjaf/khatru v0.1.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/nbd-wtf/go-nostr v0.25.0

View File

@ -1,7 +1,6 @@
package main
import (
"context"
"net/http"
"github.com/nbd-wtf/go-nostr/nip19"
@ -10,18 +9,11 @@ import (
func inviteTreeHandler(w http.ResponseWriter, r *http.Request) {
content := inviteTreePageHTML(r.Context(), InviteTreePageParams{
LoggedUser: getLoggedUser(r),
loggedUser: getLoggedUser(r),
})
htmlgo.Fprint(w, baseHTML(content), r.Context())
}
func getUserRowHandler(w http.ResponseWriter, r *http.Request) {
pubkey := r.PostFormValue("pubkey")
profile := fetchAndStoreProfile(r.Context(), pubkey)
content := userRowComponent(r.Context(), profile, getLoggedUser(r))
htmlgo.Fprint(w, content, r.Context())
}
func addToWhitelistHandler(w http.ResponseWriter, r *http.Request) {
loggedUser := getLoggedUser(r)
@ -34,6 +26,7 @@ func addToWhitelistHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "failed to add to whitelist: "+err.Error(), 500)
return
}
content := inviteTreeComponent(r.Context(), "", loggedUser)
htmlgo.Fprint(w, content, r.Context())
}
@ -162,7 +155,7 @@ func reportsViewerHandler(w http.ResponseWriter, r *http.Request) {
func homePageHandler(w http.ResponseWriter, r *http.Request) {
content := homePageHTML(r.Context(), HomePageParams{
RelayOwnerInfo: fetchAndStoreProfile(context.Background(), s.RelayPubkey),
relayOwnerInfo: fetchAndStoreProfile(r.Context(), s.RelayPubkey),
})
htmlgo.Fprint(w, baseHTML(content), r.Context())
}

19
main.go
View File

@ -6,7 +6,7 @@ import (
"net/url"
"os"
"github.com/fiatjaf/eventstore/badgern"
"github.com/fiatjaf/eventstore/badger"
"github.com/fiatjaf/khatru"
"github.com/kelseyhightower/envconfig"
"github.com/nbd-wtf/go-nostr"
@ -25,7 +25,7 @@ type Settings struct {
var (
s Settings
db = badgern.BadgerBackend{}
db = badger.BadgerBackend{}
log = zerolog.New(os.Stderr).Output(zerolog.ConsoleWriter{Out: os.Stdout}).With().Timestamp().Logger()
whitelist = make(Whitelist)
relay = khatru.NewRelay()
@ -52,18 +52,19 @@ func main() {
relay.Description = s.RelayDescription
relay.Contact = s.RelayContact
// load whitelist storage
relay.StoreEvent = append(relay.StoreEvent, db.SaveEvent)
relay.QueryEvents = append(relay.QueryEvents, db.QueryEvents)
relay.CountEvents = append(relay.CountEvents, db.CountEvents)
relay.DeleteEvent = append(relay.DeleteEvent, db.DeleteEvent)
relay.RejectEvent = append(relay.RejectEvent, rejectEventsFromUsersNotInWhitelist)
// load users registry
if err := loadWhitelist(); err != nil {
log.Fatal().Err(err).Msg("failed to load whitelist")
return
}
relay.StoreEvent = append(relay.StoreEvent, db.SaveEvent)
relay.QueryEvents = append(relay.QueryEvents, db.QueryEvents)
relay.CountEvents = append(relay.CountEvents, db.CountEvents)
relay.DeleteEvent = append(relay.DeleteEvent, db.DeleteEvent)
relay.Router().HandleFunc("/get-user-row", getUserRowHandler)
// http routes
relay.Router().HandleFunc("/add-to-whitelist", addToWhitelistHandler)
relay.Router().HandleFunc("/remove-from-whitelist", removeFromWhitelistHandler)
relay.Router().HandleFunc("/reports", reportsViewerHandler)

View File

@ -30,8 +30,11 @@ func baseHTML(inside HTMLComponent) HTMLComponent {
A().Text("information").Href("/").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"),
A().Text("invite tree").Href("/users").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"),
A().Text("reports").Href("/reports").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"),
A().Text("login").Href("#").Class(navItemClass).
Attr("_", "on click get window.nostr.signEvent({created_at: Math.round(Date.now()/1000), kind: 27235, tags: [['domain', "+s.Domain+"]], content: ''}) then get JSON.stringify(it) then set cookies['nip98'] to it"),
A().Text("").Href("#").Class(navItemClass).
Attr("_", `
on click if my innerText is equal to "login" get window.nostr.signEvent({created_at: Math.round(Date.now()/1000), kind: 27235, tags: [['domain', "`+s.Domain+`"]], content: ''}) then get JSON.stringify(it) then set cookies['nip98'] to it otherwise call cookies.clear('nip98') end then trigger load on me
on load get cookies['nip98'] then if it is undefined set my innerText to "login" otherwise set my innerText to "logout"`),
).Class("flex flex-1 items-center justify-center"),
Main(inside).Class("m-4"),
).Class("mx-4 my-6"),
@ -39,7 +42,7 @@ func baseHTML(inside HTMLComponent) HTMLComponent {
}
type HomePageParams struct {
RelayOwnerInfo sdk.ProfileMetadata
relayOwnerInfo sdk.ProfileMetadata
}
func homePageHTML(ctx context.Context, params HomePageParams) HTMLComponent {
@ -59,33 +62,43 @@ func homePageHTML(ctx context.Context, params HomePageParams) HTMLComponent {
contact,
Div(
Text("relay master: "),
A().Text(params.RelayOwnerInfo.Name).Href("nostr:"+params.RelayOwnerInfo.Npub()),
userNameComponent(ctx, params.relayOwnerInfo),
),
Br(),
Div(
Text("this relay uses"),
A().Target("_blank").Href("https://github.com/github-tijlxyz/khatru-invite").Text("Khatru Invite"),
A().Target("_blank").Href("https://github.com/github-tijlxyz/khatru-invite").Text("Khatru Invite").
Class("underline"),
Text(" which is built with "),
A().Target("_blank").Href("https://github.com/fiatjaf/khatru").Text("Khatru"),
),
A().Target("_blank").Href("https://github.com/fiatjaf/khatru").Text("Khatru").
Class("underline"),
).Class("text-sm"),
)
}
type InviteTreePageParams struct {
LoggedUser string
loggedUser string
}
func inviteTreePageHTML(ctx context.Context, params InviteTreePageParams) HTMLComponent {
return Form(
Input("pubkey").Type("text").Placeholder("npub1...").Class("w-96 rounded-md border-0 p-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600"),
Button("invite").Class(buttonClass+" p-2 bg-white hover:bg-gray-50"),
inviteForm := Div()
if params.loggedUser != "" {
inviteForm = Form(
Input("pubkey").Type("text").Placeholder("npub1...").Class("w-96 rounded-md border-0 p-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600"),
Button("invite").Class(buttonClass+" p-2 bg-white hover:bg-gray-50"),
).Attr(
"hx-post", "/add-to-whitelist",
"hx-trigger", "submit",
"hx-target", "#tree",
"_", "on htmx:afterRequest(elt, successful) if successful and elt is I call I.reset()",
)
}
return Div(
inviteForm,
Div(
inviteTreeComponent(ctx, "", params.LoggedUser),
inviteTreeComponent(ctx, "", params.loggedUser),
).Id("tree").Class("mt-3"),
).Attr(
"hx-post", "/add-to-whitelist",
"hx-trigger", "submit",
"hx-target", "#tree",
"_", "on htmx:afterRequest(elt, successful) if successful and elt is I call I.reset()",
)
}

14
relay.go Normal file
View File

@ -0,0 +1,14 @@
package main
import (
"context"
"github.com/nbd-wtf/go-nostr"
)
func rejectEventsFromUsersNotInWhitelist(ctx context.Context, event *nostr.Event) (reject bool, msg string) {
if isPublicKeyInWhitelist(event.PubKey) {
return false, ""
}
return true, "not authorized"
}

View File

@ -13,9 +13,19 @@ const WHITELIST_FILE = "users.json"
type Whitelist map[string]string // { [user_pubkey]: [invited_by] }
func addToWhitelist(pubkey string, inviter string) error {
if nostr.IsValidPublicKeyHex(pubkey) && isPublicKeyInWhitelist(inviter) && !isPublicKeyInWhitelist(pubkey) {
whitelist[pubkey] = inviter
if !isPublicKeyInWhitelist(inviter) {
return fmt.Errorf("pubkey %s doesn't have permission to invite", inviter)
}
if !nostr.IsValidPublicKeyHex(pubkey) {
return fmt.Errorf("pubkey invalid: %s", pubkey)
}
if isPublicKeyInWhitelist(pubkey) {
return fmt.Errorf("pubkey already in whitelist: %s", pubkey)
}
whitelist[pubkey] = inviter
return saveWhitelist()
}