stuff
This commit is contained in:
parent
d982ce13f5
commit
b5e2c83fba
9 changed files with 113 additions and 63 deletions
|
@ -53,9 +53,11 @@ var (
|
|||
errHTTPBadRequestMatrixMessageInvalid = &errHTTP{40019, http.StatusBadRequest, "invalid request: Matrix JSON invalid", "https://ntfy.sh/docs/publish/#matrix-gateway"}
|
||||
errHTTPBadRequestMatrixPushkeyBaseURLMismatch = &errHTTP{40020, http.StatusBadRequest, "invalid request: push key must be prefixed with base URL", "https://ntfy.sh/docs/publish/#matrix-gateway"}
|
||||
errHTTPBadRequestIconURLInvalid = &errHTTP{40021, http.StatusBadRequest, "invalid request: icon URL is invalid", "https://ntfy.sh/docs/publish/#icons"}
|
||||
errHTTPBadRequestSignupNotEnabled = &errHTTP{40022, http.StatusBadRequest, "invalid request: signup not enabled", "https://ntfy.sh/docs/config"}
|
||||
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", ""}
|
||||
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication"}
|
||||
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication"}
|
||||
errHTTPConflictUserExists = &errHTTP{40901, http.StatusConflict, "conflict: user already exists", ""}
|
||||
errHTTPEntityTooLargeAttachmentTooLarge = &errHTTP{41301, http.StatusRequestEntityTooLarge, "attachment too large, or bandwidth limit reached", "https://ntfy.sh/docs/publish/#limitations"}
|
||||
errHTTPEntityTooLargeMatrixRequestTooLarge = &errHTTP{41302, http.StatusRequestEntityTooLarge, "Matrix request is larger than the max allowed length", ""}
|
||||
errHTTPTooManyRequestsLimitRequests = &errHTTP{42901, http.StatusTooManyRequests, "limit reached: too many requests, please be nice", "https://ntfy.sh/docs/publish/#limitations"}
|
||||
|
|
|
@ -47,6 +47,7 @@ import (
|
|||
purge accounts that were not logged into in X
|
||||
sync subscription display name
|
||||
store users
|
||||
signup: check unique user
|
||||
Pages:
|
||||
- Home
|
||||
- Password reset
|
||||
|
@ -1307,7 +1308,17 @@ func (s *Server) sendDelayedMessages() error {
|
|||
return err
|
||||
}
|
||||
for _, m := range messages {
|
||||
v := s.visitorFromID(fmt.Sprintf("ip:%s", m.Sender.String()), m.Sender, nil) // FIXME: This is wrong wrong wrong
|
||||
var v *visitor
|
||||
if m.User != "" {
|
||||
user, err := s.auth.User(m.User)
|
||||
if err != nil {
|
||||
log.Warn("%s Error sending delayed message: %s", logMessagePrefix(v, m), err.Error())
|
||||
continue
|
||||
}
|
||||
v = s.visitorFromUser(user, m.Sender)
|
||||
} else {
|
||||
v = s.visitorFromIP(m.Sender)
|
||||
}
|
||||
if err := s.sendDelayedMessage(v, m); err != nil {
|
||||
log.Warn("%s Error sending delayed message: %s", logMessagePrefix(v, m), err.Error())
|
||||
}
|
||||
|
@ -1462,18 +1473,18 @@ func (s *Server) autorizeTopic(next handleFunc, perm auth.Permission) handleFunc
|
|||
// visitor creates or retrieves a rate.Limiter for the given visitor.
|
||||
// Note that this function will always return a visitor, even if an error occurs.
|
||||
func (s *Server) visitor(r *http.Request) (v *visitor, err error) {
|
||||
ip := s.extractIPAddress(r)
|
||||
visitorID := fmt.Sprintf("ip:%s", ip.String())
|
||||
ip := extractIPAddress(r, s.config.BehindProxy)
|
||||
var user *auth.User // may stay nil if no auth header!
|
||||
if user, err = s.authenticate(r); err != nil {
|
||||
log.Debug("authentication failed: %s", err.Error())
|
||||
err = errHTTPUnauthorized // Always return visitor, even when error occurs!
|
||||
}
|
||||
if user != nil {
|
||||
visitorID = fmt.Sprintf("user:%s", user.Name)
|
||||
v = s.visitorFromUser(user, ip)
|
||||
} else {
|
||||
v = s.visitorFromIP(ip)
|
||||
}
|
||||
v = s.visitorFromID(visitorID, ip, user)
|
||||
v.user = user // Update user -- FIXME this is ugly, do "newVisitorFromUser" instead
|
||||
v.user = user // Update user -- FIXME race?
|
||||
return v, err // Always return visitor, even when error occurs!
|
||||
}
|
||||
|
||||
|
@ -1526,30 +1537,10 @@ func (s *Server) visitorFromID(visitorID string, ip netip.Addr, user *auth.User)
|
|||
return v
|
||||
}
|
||||
|
||||
func (s *Server) extractIPAddress(r *http.Request) netip.Addr {
|
||||
remoteAddr := r.RemoteAddr
|
||||
addrPort, err := netip.ParseAddrPort(remoteAddr)
|
||||
ip := addrPort.Addr()
|
||||
if err != nil {
|
||||
// This should not happen in real life; only in tests. So, using falling back to 0.0.0.0 if address unspecified
|
||||
ip, err = netip.ParseAddr(remoteAddr)
|
||||
if err != nil {
|
||||
ip = netip.IPv4Unspecified()
|
||||
log.Warn("unable to parse IP (%s), new visitor with unspecified IP (0.0.0.0) created %s", remoteAddr, err)
|
||||
}
|
||||
}
|
||||
if s.config.BehindProxy && strings.TrimSpace(r.Header.Get("X-Forwarded-For")) != "" {
|
||||
// X-Forwarded-For can contain multiple addresses (see #328). If we are behind a proxy,
|
||||
// only the right-most address can be trusted (as this is the one added by our proxy server).
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For for details.
|
||||
ips := util.SplitNoEmpty(r.Header.Get("X-Forwarded-For"), ",")
|
||||
realIP, err := netip.ParseAddr(strings.TrimSpace(util.LastString(ips, remoteAddr)))
|
||||
if err != nil {
|
||||
log.Error("invalid IP address %s received in X-Forwarded-For header: %s", ip, err.Error())
|
||||
// Fall back to regular remote address if X-Forwarded-For is damaged
|
||||
} else {
|
||||
ip = realIP
|
||||
}
|
||||
}
|
||||
return ip
|
||||
func (s *Server) visitorFromIP(ip netip.Addr) *visitor {
|
||||
return s.visitorFromID(fmt.Sprintf("ip:%s", ip.String()), ip, nil)
|
||||
}
|
||||
|
||||
func (s *Server) visitorFromUser(user *auth.User, ip netip.Addr) *visitor {
|
||||
return s.visitorFromID(fmt.Sprintf("user:%s", user.Name), ip, user)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ func (s *Server) handleAccountCreate(w http.ResponseWriter, r *http.Request, v *
|
|||
signupAllowed := s.config.EnableSignup
|
||||
admin := v.user != nil && v.user.Role == auth.RoleAdmin
|
||||
if !signupAllowed && !admin {
|
||||
return errHTTPUnauthorized
|
||||
return errHTTPBadRequestSignupNotEnabled
|
||||
}
|
||||
body, err := util.Peek(r.Body, 4096) // FIXME
|
||||
if err != nil {
|
||||
|
@ -23,6 +23,9 @@ func (s *Server) handleAccountCreate(w http.ResponseWriter, r *http.Request, v *
|
|||
if err := json.NewDecoder(body).Decode(&newAccount); err != nil {
|
||||
return err
|
||||
}
|
||||
if existingUser, _ := s.auth.User(newAccount.Username); existingUser != nil {
|
||||
return errHTTPConflictUserExists
|
||||
}
|
||||
if err := s.auth.AddUser(newAccount.Username, newAccount.Password, auth.RoleUser); err != nil { // TODO this should return a User
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@ package server
|
|||
import (
|
||||
"fmt"
|
||||
"github.com/emersion/go-smtp"
|
||||
"heckel.io/ntfy/log"
|
||||
"heckel.io/ntfy/util"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
@ -89,3 +91,31 @@ func renderHTTPRequest(r *http.Request) string {
|
|||
r.Body = body // Important: Reset body, so it can be re-read
|
||||
return strings.TrimSpace(lines)
|
||||
}
|
||||
|
||||
func extractIPAddress(r *http.Request, behindProxy bool) netip.Addr {
|
||||
remoteAddr := r.RemoteAddr
|
||||
addrPort, err := netip.ParseAddrPort(remoteAddr)
|
||||
ip := addrPort.Addr()
|
||||
if err != nil {
|
||||
// This should not happen in real life; only in tests. So, using falling back to 0.0.0.0 if address unspecified
|
||||
ip, err = netip.ParseAddr(remoteAddr)
|
||||
if err != nil {
|
||||
ip = netip.IPv4Unspecified()
|
||||
log.Warn("unable to parse IP (%s), new visitor with unspecified IP (0.0.0.0) created %s", remoteAddr, err)
|
||||
}
|
||||
}
|
||||
if behindProxy && strings.TrimSpace(r.Header.Get("X-Forwarded-For")) != "" {
|
||||
// X-Forwarded-For can contain multiple addresses (see #328). If we are behind a proxy,
|
||||
// only the right-most address can be trusted (as this is the one added by our proxy server).
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For for details.
|
||||
ips := util.SplitNoEmpty(r.Header.Get("X-Forwarded-For"), ",")
|
||||
realIP, err := netip.ParseAddr(strings.TrimSpace(util.LastString(ips, remoteAddr)))
|
||||
if err != nil {
|
||||
log.Error("invalid IP address %s received in X-Forwarded-For header: %s", ip, err.Error())
|
||||
// Fall back to regular remote address if X-Forwarded-For is damaged
|
||||
} else {
|
||||
ip = realIP
|
||||
}
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue