From 15794baee620edafdfe1dae08b65fb6d8e2b65a4 Mon Sep 17 00:00:00 2001 From: Gregor Pogacnik <1640719+fiksn@users.noreply.github.com> Date: Wed, 19 Jan 2022 00:50:47 +0100 Subject: [PATCH] Allow multiple comma-separated domains --- README.md | 2 ++ api.go | 42 ++++++++++++++++++------- db.go | 81 +++++++++++++++++++++++++++++++++++++++++------- grab.html | 2 +- html.go | 16 +++++++--- index.html | 8 +++-- lnurl.go | 36 +++++++++++++++++---- main.go | 66 +++++++++++++++++++++++++++++---------- makeinvoice.go | 4 +-- static/style.css | 30 ++++++++++++++++++ 10 files changed, 231 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index fa97ed6..554faf2 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Federated Lightning Address Server 1. Download the binary from the releases page (or compile with `go build` or `go get`) 2. Set the following environment variables somehow (using example values from bitmia.com): +(note that DOMAIN can be a comma-seperated list or a single domain, when using multiple domains +you need to make sure "Host" HTTP header is forwarded to satdress process if you have some reverse-proxy) ``` PORT=17422 diff --git a/api.go b/api.go index d6a9ade..4ca9cc5 100644 --- a/api.go +++ b/api.go @@ -18,6 +18,7 @@ type Response struct { type SuccessClaim struct { Name string `json:"name"` + Domain string `json:"domain"` PIN string `json:"pin"` Invoice string `json:"invoice"` } @@ -25,7 +26,7 @@ type SuccessClaim struct { // not authenticated, if correct pin is provided call returns the SuccessClaim func ClaimAddress(w http.ResponseWriter, r *http.Request) { params := parseParams(r) - pin, inv, err := SaveName(params.Name, params, params.Pin) + pin, inv, err := SaveName(params.Name, params.Domain, params, params.Pin) if err != nil { sendError(w, 400, "could not register name: %s", err.Error()) return @@ -33,8 +34,8 @@ func ClaimAddress(w http.ResponseWriter, r *http.Request) { response := Response{ Ok: true, - Message: fmt.Sprintf("claimed %v@%v", params.Name, s.Domain), - Data: SuccessClaim{params.Name, pin, inv}, + Message: fmt.Sprintf("claimed %v@%v", params.Name, params.Domain), + Data: SuccessClaim{params.Name, params.Domain, pin, inv}, } // TODO: middleware for responses that adds this header @@ -45,18 +46,19 @@ func ClaimAddress(w http.ResponseWriter, r *http.Request) { func GetUser(w http.ResponseWriter, r *http.Request) { name := mux.Vars(r)["name"] - params, err := GetName(name) + domain := mux.Vars(r)["domain"] + params, err := GetName(name, domain) if err != nil { sendError(w, 400, err.Error()) return } // add pin to response because sometimes not saved in database; after first call to /api/v1/claim - params.Pin = ComputePIN(name) + params.Pin = ComputePIN(name, domain) response := Response{ Ok: true, - Message: fmt.Sprintf("%v@%v found", params.Name, s.Domain), + Message: fmt.Sprintf("%v@%v found", params.Name, domain), Data: params, } @@ -68,6 +70,7 @@ func GetUser(w http.ResponseWriter, r *http.Request) { func UpdateUser(w http.ResponseWriter, r *http.Request) { params := parseParams(r) name := mux.Vars(r)["name"] + domain := mux.Vars(r)["domain"] // if pin not in json request body get it from header if params.Pin == "" { @@ -75,12 +78,12 @@ func UpdateUser(w http.ResponseWriter, r *http.Request) { params.Pin = r.Header.Get("X-Pin") } - if _, _, err := SaveName(name, params, params.Pin); err != nil { + if _, _, err := SaveName(name, domain, params, params.Pin); err != nil { sendError(w, 500, err.Error()) return } - updatedParams, err := GetName(name) + updatedParams, err := GetName(name, domain) if err != nil { sendError(w, 500, err.Error()) return @@ -89,7 +92,7 @@ func UpdateUser(w http.ResponseWriter, r *http.Request) { // return the updated values or just http.StatusCreated? response := Response{ Ok: true, - Message: fmt.Sprintf("updated %v@%v parameters", params.Name, s.Domain), + Message: fmt.Sprintf("updated %v@%v parameters", params.Name, domain), Data: updatedParams, } @@ -100,14 +103,15 @@ func UpdateUser(w http.ResponseWriter, r *http.Request) { func DeleteUser(w http.ResponseWriter, r *http.Request) { name := mux.Vars(r)["name"] - if err := DeleteName(name); err != nil { + domain := mux.Vars(r)["domain"] + if err := DeleteName(name, domain); err != nil { sendError(w, 500, err.Error()) return } response := Response{ Ok: true, - Message: fmt.Sprintf("deleted %v@%v", name, s.Domain), + Message: fmt.Sprintf("deleted %v@%v", name, domain), Data: nil, } @@ -119,6 +123,20 @@ func DeleteUser(w http.ResponseWriter, r *http.Request) { // authentication middleware func authenticate(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // check domain + domain := mux.Vars(r)["domain"] + available := getDomains(s.Domain) + found := false + for _, one := range available { + if one == domain { + found = true + } + } + if !found { + sendError(w, 400, "could not use domain: %s", domain) + return + } + // exempt /claim from authentication check; if strings.HasPrefix(r.URL.Path, "/api/v1/claim") { next.ServeHTTP(w, r) @@ -136,7 +154,7 @@ func authenticate(next http.Handler) http.Handler { providedPin = parseParams(r).Pin } - if providedPin != ComputePIN(name) { + if providedPin != ComputePIN(name, domain) { err = fmt.Errorf("wrong pin") } diff --git a/db.go b/db.go index 15b8fa4..411dca0 100644 --- a/db.go +++ b/db.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "fmt" + "os" "strings" "github.com/cockroachdb/pebble" @@ -14,6 +15,7 @@ import ( type Params struct { Name string `json:"name"` + Domain string `json:"domain,omitempty"` Kind string `json:"kind"` Host string `json:"host"` Key string `json:"key"` @@ -26,13 +28,16 @@ type Params struct { func SaveName( name string, + domain string, params *Params, providedPin string, ) (pin string, inv string, err error) { name = strings.ToLower(name) - key := []byte(name) + domain = strings.ToLower(domain) - pin = ComputePIN(name) + key := []byte(getID(name, domain)) + + pin = ComputePIN(name, domain) if _, closer, err := db.Get(key); err == nil { defer closer.Close() @@ -45,6 +50,7 @@ func SaveName( } params.Name = name + params.Domain = domain // check if the given data works if inv, err = makeInvoice(params, 1000, &pin); err != nil { @@ -60,10 +66,8 @@ func SaveName( return pin, inv, nil } -func GetName(name string) (*Params, error) { - name = strings.ToLower(name) - - val, closer, err := db.Get([]byte(name)) +func GetName(name, domain string) (*Params, error) { + val, closer, err := db.Get([]byte(getID(name, domain))) if err != nil { return nil, err } @@ -75,12 +79,12 @@ func GetName(name string) (*Params, error) { } params.Name = name + params.Domain = domain return ¶ms, nil } -func DeleteName(name string) error { - name = strings.ToLower(name) - key := []byte(name) +func DeleteName(name, domain string) error { + key := []byte(getID(name, domain)) if err := db.Delete(key, pebble.Sync); err != nil { return err @@ -89,9 +93,62 @@ func DeleteName(name string) error { return nil } -func ComputePIN(name string) string { - name = strings.ToLower(name) +func ComputePIN(name, domain string) string { mac := hmac.New(sha256.New, []byte(s.Secret)) - mac.Write([]byte(name + "@" + s.Domain)) + mac.Write([]byte(getID(name, domain))) return hex.EncodeToString(mac.Sum(nil)) } + +func getID(name, domain string) string { + if s.GlobalUsers { + return strings.ToLower(name) + } else { + return strings.ToLower(fmt.Sprintf("%s@%s", name, domain)) + } +} + +func tryMigrate(old, new string) { + if _, err := os.Stat(old); os.IsNotExist(err) { + return + } + + log.Info().Str("db", old).Msg("Migrating db") + + newDb, err := pebble.Open(new, nil) + if err != nil { + log.Fatal().Err(err).Str("path", new).Msg("failed to open db.") + } + defer newDb.Close() + + oldDb, err := pebble.Open(old, nil) + if err != nil { + log.Fatal().Err(err).Str("path", old).Msg("failed to open db.") + } + defer oldDb.Close() + + iter := oldDb.NewIter(nil) + defer iter.Close() + + for iter.First(); iter.Valid(); iter.Next() { + log.Debug().Str("db", string(iter.Key())).Msg("Migrating key") + var params Params + if err := json.Unmarshal(iter.Value(), ¶ms); err != nil { + log.Debug().Err(err).Msg("Unmarshal error") + continue + } + + params.Domain = old // old database name was domain + + // save it + data, err := json.Marshal(params) + if err != nil { + log.Debug().Err(err).Msg("Marshal error") + continue + } + + if err := newDb.Set([]byte(getID(params.Name, params.Domain)), data, pebble.Sync); err != nil { + log.Debug().Err(err).Msg("Set error") + continue + } + } +} diff --git a/grab.html b/grab.html index 2a3b5d5..6adef28 100644 --- a/grab.html +++ b/grab.html @@ -13,7 +13,7 @@