From fb9eef23d3121e08e66bd26f995b2af5616853b4 Mon Sep 17 00:00:00 2001 From: mleku Date: Fri, 29 Nov 2024 09:31:38 +0000 Subject: [PATCH] update handling to not require auth unless path is auth required on web server --- realy/admin.go | 133 +++++++++++++++++++++------------------ realy/handlerelayinfo.go | 2 +- realy/version | 2 +- relayinfo/types.go | 20 +++--- 4 files changed, 85 insertions(+), 72 deletions(-) diff --git a/realy/admin.go b/realy/admin.go index 8b6f23a..b8fd55a 100644 --- a/realy/admin.go +++ b/realy/admin.go @@ -13,7 +13,7 @@ import ( "realy.lol/sha256" ) -func (s *Server) HandleAdmin(w http.ResponseWriter, r *http.Request) { +func (s *Server) HTTPAuth(r *http.Request) (authed bool) { username, password, ok := r.BasicAuth() if ok { log.I.S(username, password) @@ -35,72 +35,85 @@ func (s *Server) HandleAdmin(w http.ResponseWriter, r *http.Request) { expectedUsernameHash[:]) == 1 passwordMatch := subtle.ConstantTimeCompare(passwordHash[:], expectedPasswordHash[:]) == 1 + if usernameMatch && passwordMatch { + return true + } + } + return +} - // If the username and password are correct, then call - // the next handler in the chain. Make sure to return - // afterwards, so that none of the code below is run. - if !usernameMatch || !passwordMatch { - w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`) - http.Error(w, "Unauthorized", http.StatusUnauthorized) +func (s *Server) AuthFail(w http.ResponseWriter) { + w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`) + http.Error(w, "Unauthorized", http.StatusUnauthorized) +} + +func (s *Server) HandleAdmin(w http.ResponseWriter, r *http.Request) { + switch { + case strings.HasPrefix(r.URL.Path, "/export"): + if ok := s.HTTPAuth(r); !ok { + s.AuthFail(w) return } - switch { - case strings.HasPrefix(r.URL.Path, "/export"): - log.I.F("export of event data requested on admin port") - store := s.relay.Storage(context.Bg()) - if strings.Count(r.URL.Path, "/") > 1 { - split := strings.Split(r.URL.Path, "/") - // there should be 3 for a valid path, an empty, "export" and the final parameter - if len(split) != 3 { - fmt.Fprintf(w, "incorrectly formatted export parameter: '%s'", - r.URL.Path) - return - } - switch split[2] { - case "users": - // todo: naughty reaching through interface here lol... but the relay - // implementation does have this feature and another impl may not. Perhaps add - // a new interface for grabbing the relay's allowed list, and rename things to - // be more clear. And add a method for fetching such a relay's allowed writers. - if rl, ok := s.relay.(*app.Relay); ok { - follows := make([]B, 0, len(rl.Followed)) - for f := range rl.Followed { - follows = append(follows, B(f)) - } - store.Export(s.Ctx, w, follows...) + log.I.F("export of event data requested on admin port") + store := s.relay.Storage(context.Bg()) + if strings.Count(r.URL.Path, "/") > 1 { + split := strings.Split(r.URL.Path, "/") + // there should be 3 for a valid path, an empty, "export" and the final parameter + if len(split) != 3 { + fmt.Fprintf(w, "incorrectly formatted export parameter: '%s'", + r.URL.Path) + return + } + switch split[2] { + case "users": + // todo: naughty reaching through interface here lol... but the relay + // implementation does have this feature and another impl may not. Perhaps add + // a new interface for grabbing the relay's allowed list, and rename things to + // be more clear. And add a method for fetching such a relay's allowed writers. + if rl, ok := s.relay.(*app.Relay); ok { + follows := make([]B, 0, len(rl.Followed)) + for f := range rl.Followed { + follows = append(follows, B(f)) } - default: - // this should be a hyphen separated list of hexadecimal pubkey values - var exportPubkeys []B - pubkeys := strings.Split(split[2], "-") - for _, pubkey := range pubkeys { - // check they are valid hex - pk, err := hex.Dec(pubkey) - if err != nil { - log.E.F("invalid public key '%s' in parameters", pubkey) - continue - } - exportPubkeys = append(exportPubkeys, pk) + store.Export(s.Ctx, w, follows...) + } + default: + // this should be a hyphen separated list of hexadecimal pubkey values + var exportPubkeys []B + pubkeys := strings.Split(split[2], "-") + for _, pubkey := range pubkeys { + // check they are valid hex + pk, err := hex.Dec(pubkey) + if err != nil { + log.E.F("invalid public key '%s' in parameters", pubkey) + continue } - store.Export(s.Ctx, w, exportPubkeys...) + exportPubkeys = append(exportPubkeys, pk) } - } else { - store.Export(s.Ctx, w) + store.Export(s.Ctx, w, exportPubkeys...) } - case strings.HasPrefix(r.URL.Path, "/import"): - log.I.F("import of event data requested on admin port %s", r.RequestURI) - store := s.relay.Storage(context.Bg()) - read := io.LimitReader(r.Body, r.ContentLength) - store.Import(read) - case strings.HasPrefix(r.URL.Path, "/shutdown"): - fmt.Fprintf(w, "shutting down") - defer r.Body.Close() - s.Shutdown() - default: - fmt.Fprintf(w, `todo: make help info page`) + } else { + store.Export(s.Ctx, w) + } + case strings.HasPrefix(r.URL.Path, "/import"): + if ok := s.HTTPAuth(r); !ok { + s.AuthFail(w) + return + } + log.I.F("import of event data requested on admin port %s", r.RequestURI) + store := s.relay.Storage(context.Bg()) + read := io.LimitReader(r.Body, r.ContentLength) + store.Import(read) + case strings.HasPrefix(r.URL.Path, "/shutdown"): + if ok := s.HTTPAuth(r); !ok { + s.AuthFail(w) + return } - } else { - w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`) - http.Error(w, "Unauthorized", http.StatusUnauthorized) + fmt.Fprintf(w, "shutting down") + defer r.Body.Close() + s.Shutdown() + default: + fmt.Fprintf(w, "todo: realy web interface page\n\n") + s.HandleNIP11(w, r) } } diff --git a/realy/handlerelayinfo.go b/realy/handlerelayinfo.go index 797848b..526888a 100644 --- a/realy/handlerelayinfo.go +++ b/realy/handlerelayinfo.go @@ -50,7 +50,7 @@ func (s *Server) HandleNIP11(w http.ResponseWriter, r *http.Request) { Nips: supportedNIPs, Software: "https://realy.lol", Version: version, - Limitation: ri.Limits{MaxLimit: s.maxLimit}, + Limitation: ri.Limits{MaxLimit: s.maxLimit, AuthRequired: s.authRequired}, Icon: "https://cdn.satellite.earth/ac9778868fbf23b63c47c769a74e163377e6ea94d3f0f31711931663d035c4f6.png", } } diff --git a/realy/version b/realy/version index b3257b5..8c8521e 100644 --- a/realy/version +++ b/realy/version @@ -1 +1 @@ -v1.2.20 \ No newline at end of file +v1.2.21 \ No newline at end of file diff --git a/relayinfo/types.go b/relayinfo/types.go index 772ffe4..bc6ff81 100644 --- a/relayinfo/types.go +++ b/relayinfo/types.go @@ -219,20 +219,20 @@ type Pub struct { // T is the realy information document. type T struct { Name string `json:"name"` - Description string `json:"description"` - PubKey string `json:"pubkey"` + Description string `json:"description,omitempty"` + PubKey string `json:"pubkey,omitempty"` Contact string `json:"contact,omitempty"` Nips number.List `json:"supported_nips"` Software string `json:"software"` Version string `json:"version"` - Limitation Limits `json:"limitation"` - Retention any `json:"retention"` - RelayCountries []string `json:"relay_countries"` - LanguageTags []string `json:"language_tags"` - Tags []string `json:"tags"` - PostingPolicy string `json:"posting_policy"` - PaymentsURL string `json:"payments_url"` - Fees Fees `json:"fees"` + Limitation Limits `json:"limitation,omitempty"` + Retention any `json:"retention,omitempty"` + RelayCountries []string `json:"relay_countries,omitempty"` + LanguageTags []string `json:"language_tags,omitempty"` + Tags []string `json:"tags,omitempty"` + PostingPolicy string `json:"posting_policy,omitempty"` + PaymentsURL string `json:"payments_url,omitempty"` + Fees *Fees `json:"fees,omitempty"` Icon string `json:"icon"` sync.Mutex }