1
0
Fork 0
mirror of https://github.com/binwiederhier/ntfy.git synced 2025-05-30 02:15:40 +02:00

Fix almost all tests

This commit is contained in:
binwiederhier 2022-12-27 22:14:14 -05:00
parent 95a8e64fbb
commit d9722a9825
17 changed files with 197 additions and 253 deletions
server

View file

@ -43,12 +43,15 @@ import (
"user list" shows * twice
"ntfy access everyone user4topic <bla>" twice -> UNIQUE constraint error
Account usage not updated "in real time"
Attachment expiration based on plan
Plan: Keep 10000 messages or keep X days?
Sync:
- "mute" setting
- figure out what settings are "web" or "phone"
UI:
- Subscription dotmenu dropdown: Move to nav bar, or make same as profile dropdown
- "Logout and delete local storage" option
- Delete local storage when deleting account
Pages:
- Home
- Password reset
@ -61,7 +64,8 @@ import (
- APIs
- CRUD tokens
- Expire tokens
-
- userManager can be nil
- visitor with/without user
*/
// Server is the main server, providing the UI and API for ntfy
@ -77,7 +81,7 @@ type Server struct {
visitors map[string]*visitor // ip:<ip> or user:<user>
firebaseClient *firebaseClient
messages int64
userManager user.Manager
userManager *user.Manager // Might be nil!
messageCache *messageCache
fileCache *fileCache
closeChan chan bool
@ -165,9 +169,9 @@ func New(conf *Config) (*Server, error) {
return nil, err
}
}
var auther user.Manager
var userManager *user.Manager
if conf.AuthFile != "" {
auther, err = user.NewSQLiteAuthManager(conf.AuthFile, conf.AuthDefaultRead, conf.AuthDefaultWrite)
userManager, err = user.NewManager(conf.AuthFile, conf.AuthDefaultRead, conf.AuthDefaultWrite)
if err != nil {
return nil, err
}
@ -178,7 +182,7 @@ func New(conf *Config) (*Server, error) {
if err != nil {
return nil, err
}
firebaseClient = newFirebaseClient(sender, auther)
firebaseClient = newFirebaseClient(sender, userManager)
}
return &Server{
config: conf,
@ -187,7 +191,7 @@ func New(conf *Config) (*Server, error) {
firebaseClient: firebaseClient,
smtpSender: mailer,
topics: topics,
userManager: auther,
userManager: userManager,
visitors: make(map[string]*visitor),
}, nil
}
@ -341,27 +345,27 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit
} else if r.Method == http.MethodGet && r.URL.Path == webConfigPath {
return s.ensureWebEnabled(s.handleWebConfig)(w, r, v)
} else if r.Method == http.MethodPost && r.URL.Path == accountPath {
return s.handleAccountCreate(w, r, v)
return s.ensureAccountsEnabled(s.handleAccountCreate)(w, r, v)
} else if r.Method == http.MethodGet && r.URL.Path == accountPath {
return s.handleAccountGet(w, r, v)
return s.handleAccountGet(w, r, v) // Allowed by anonymous
} else if r.Method == http.MethodDelete && r.URL.Path == accountPath {
return s.handleAccountDelete(w, r, v)
return s.ensureWithAccount(s.handleAccountDelete)(w, r, v)
} else if r.Method == http.MethodPost && r.URL.Path == accountPasswordPath {
return s.handleAccountPasswordChange(w, r, v)
return s.ensureWithAccount(s.handleAccountPasswordChange)(w, r, v)
} else if r.Method == http.MethodPost && r.URL.Path == accountTokenPath {
return s.handleAccountTokenIssue(w, r, v)
return s.ensureWithAccount(s.handleAccountTokenIssue)(w, r, v)
} else if r.Method == http.MethodPatch && r.URL.Path == accountTokenPath {
return s.handleAccountTokenExtend(w, r, v)
return s.ensureWithAccount(s.handleAccountTokenExtend)(w, r, v)
} else if r.Method == http.MethodDelete && r.URL.Path == accountTokenPath {
return s.handleAccountTokenDelete(w, r, v)
return s.ensureWithAccount(s.handleAccountTokenDelete)(w, r, v)
} else if r.Method == http.MethodPatch && r.URL.Path == accountSettingsPath {
return s.handleAccountSettingsChange(w, r, v)
return s.ensureWithAccount(s.handleAccountSettingsChange)(w, r, v)
} else if r.Method == http.MethodPost && r.URL.Path == accountSubscriptionPath {
return s.handleAccountSubscriptionAdd(w, r, v)
return s.ensureWithAccount(s.handleAccountSubscriptionAdd)(w, r, v)
} else if r.Method == http.MethodPatch && accountSubscriptionSingleRegex.MatchString(r.URL.Path) {
return s.handleAccountSubscriptionChange(w, r, v)
return s.ensureWithAccount(s.handleAccountSubscriptionChange)(w, r, v)
} else if r.Method == http.MethodDelete && accountSubscriptionSingleRegex.MatchString(r.URL.Path) {
return s.handleAccountSubscriptionDelete(w, r, v)
return s.ensureWithAccount(s.handleAccountSubscriptionDelete)(w, r, v)
} else if r.Method == http.MethodGet && r.URL.Path == matrixPushPath {
return s.handleMatrixDiscovery(w)
} else if r.Method == http.MethodGet && staticRegex.MatchString(r.URL.Path) {
@ -804,7 +808,7 @@ func (s *Server) handleBodyAsAttachment(r *http.Request, v *visitor, m *message,
} else if m.Time > time.Now().Add(s.config.AttachmentExpiryDuration).Unix() {
return errHTTPBadRequestAttachmentsExpiryBeforeDelivery
}
stats, err := v.Stats()
stats, err := v.Info()
if err != nil {
return err
}
@ -1182,7 +1186,7 @@ func (s *Server) topicsFromIDs(ids ...string) ([]*topic, error) {
return topics, nil
}
func (s *Server) updateStatsAndPrune() {
func (s *Server) execManager() {
log.Debug("Manager: Starting")
defer log.Debug("Manager: Finished")
@ -1203,8 +1207,10 @@ func (s *Server) updateStatsAndPrune() {
log.Debug("Manager: Deleted %d stale visitor(s)", staleVisitors)
// Delete expired user tokens
if err := s.userManager.RemoveExpiredTokens(); err != nil {
log.Warn("Error expiring user tokens: %s", err.Error())
if s.userManager != nil {
if err := s.userManager.RemoveExpiredTokens(); err != nil {
log.Warn("Error expiring user tokens: %s", err.Error())
}
}
// Delete expired attachments
@ -1293,7 +1299,7 @@ func (s *Server) runManager() {
for {
select {
case <-time.After(s.config.ManagerInterval):
s.updateStatsAndPrune()
s.execManager()
case <-s.closeChan:
return
}
@ -1399,6 +1405,24 @@ func (s *Server) ensureWebEnabled(next handleFunc) handleFunc {
}
}
func (s *Server) ensureAccountsEnabled(next handleFunc) handleFunc {
return func(w http.ResponseWriter, r *http.Request, v *visitor) error {
if s.userManager != nil {
return errHTTPNotFound
}
return next(w, r, v)
}
}
func (s *Server) ensureWithAccount(next handleFunc) handleFunc {
return s.ensureAccountsEnabled(func(w http.ResponseWriter, r *http.Request, v *visitor) error {
if v.user != nil {
return errHTTPNotFound
}
return next(w, r, v)
})
}
// transformBodyJSON peeks the request body, reads the JSON, and converts it to headers
// before passing it on to the next handler. This is meant to be used in combination with handlePublish.
func (s *Server) transformBodyJSON(next handleFunc) handleFunc {
@ -1502,17 +1526,17 @@ func (s *Server) autorizeTopic(next handleFunc, perm user.Permission) handleFunc
// 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 := extractIPAddress(r, s.config.BehindProxy)
var user *user.User // may stay nil if no auth header!
if user, err = s.authenticate(r); err != nil {
var u *user.User // may stay nil if no auth header!
if u, 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 {
v = s.visitorFromUser(user, ip)
if u != nil {
v = s.visitorFromUser(u, ip)
} else {
v = s.visitorFromIP(ip)
}
v.user = user // Update user -- FIXME race?
v.user = u // Update user -- FIXME race?
return v, err // Always return visitor, even when error occurs!
}
@ -1521,17 +1545,19 @@ func (s *Server) visitor(r *http.Request) (v *visitor, err error) {
// support the WebSocket JavaScript class, which does not support passing headers during the initial request. The auth
// query param is effectively double base64 encoded. Its format is base64(Basic base64(user:pass)).
func (s *Server) authenticate(r *http.Request) (user *user.User, err error) {
value := r.Header.Get("Authorization")
value := strings.TrimSpace(r.Header.Get("Authorization"))
queryParam := readQueryParam(r, "authorization", "auth")
if queryParam != "" {
a, err := base64.RawURLEncoding.DecodeString(queryParam)
if err != nil {
return nil, err
}
value = string(a)
value = strings.TrimSpace(string(a))
}
if value == "" {
return nil, nil
} else if s.userManager == nil {
return nil, errHTTPUnauthorized
}
if strings.HasPrefix(value, "Bearer") {
return s.authenticateBearerAuth(value)