| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- package server
- import (
- "encoding/json"
- "errors"
- "heckel.io/ntfy/auth"
- "heckel.io/ntfy/util"
- "net/http"
- )
- func (s *Server) handleAccountCreate(w http.ResponseWriter, r *http.Request, v *visitor) error {
- signupAllowed := s.config.EnableSignup
- admin := v.user != nil && v.user.Role == auth.RoleAdmin
- if !signupAllowed && !admin {
- return errHTTPUnauthorized
- }
- body, err := util.Peek(r.Body, 4096) // FIXME
- if err != nil {
- return err
- }
- defer r.Body.Close()
- var newAccount apiAccountCreateRequest
- if err := json.NewDecoder(body).Decode(&newAccount); err != nil {
- return err
- }
- if err := s.auth.AddUser(newAccount.Username, newAccount.Password, auth.RoleUser); err != nil { // TODO this should return a User
- return err
- }
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
- // FIXME return something
- return nil
- }
- func (s *Server) handleAccountGet(w http.ResponseWriter, r *http.Request, v *visitor) error {
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
- stats, err := v.Stats()
- if err != nil {
- return err
- }
- response := &apiAccountSettingsResponse{
- Usage: &apiAccountUsageLimits{},
- }
- if v.user != nil {
- response.Username = v.user.Name
- response.Role = string(v.user.Role)
- if v.user.Prefs != nil {
- if v.user.Prefs.Language != "" {
- response.Language = v.user.Prefs.Language
- }
- if v.user.Prefs.Notification != nil {
- response.Notification = v.user.Prefs.Notification
- }
- if v.user.Prefs.Subscriptions != nil {
- response.Subscriptions = v.user.Prefs.Subscriptions
- }
- }
- if v.user.Plan != nil {
- response.Usage.Basis = "account"
- response.Plan = &apiAccountSettingsPlan{
- Name: v.user.Plan.Name,
- MessagesLimit: v.user.Plan.MessagesLimit,
- EmailsLimit: v.user.Plan.EmailsLimit,
- AttachmentsBytesLimit: v.user.Plan.AttachmentBytesLimit,
- }
- } else {
- if v.user.Role == auth.RoleAdmin {
- response.Usage.Basis = "account"
- response.Plan = &apiAccountSettingsPlan{
- Name: "Unlimited",
- MessagesLimit: 0,
- EmailsLimit: 0,
- AttachmentsBytesLimit: 0,
- }
- } else {
- response.Usage.Basis = "ip"
- response.Plan = &apiAccountSettingsPlan{
- Name: "Free",
- MessagesLimit: s.config.VisitorRequestLimitBurst,
- EmailsLimit: s.config.VisitorEmailLimitBurst,
- AttachmentsBytesLimit: s.config.VisitorAttachmentTotalSizeLimit,
- }
- }
- }
- } else {
- response.Username = auth.Everyone
- response.Role = string(auth.RoleAnonymous)
- response.Usage.Basis = "account"
- response.Plan = &apiAccountSettingsPlan{
- Name: "Anonymous",
- MessagesLimit: s.config.VisitorRequestLimitBurst,
- EmailsLimit: s.config.VisitorEmailLimitBurst,
- AttachmentsBytesLimit: s.config.VisitorAttachmentTotalSizeLimit,
- }
- }
- response.Usage.Messages = int(v.requests.Tokens())
- response.Usage.AttachmentsBytes = stats.VisitorAttachmentBytesUsed
- if err := json.NewEncoder(w).Encode(response); err != nil {
- return err
- }
- return nil
- }
- func (s *Server) handleAccountDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
- if v.user == nil {
- return errHTTPUnauthorized
- }
- if err := s.auth.RemoveUser(v.user.Name); err != nil {
- return err
- }
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
- // FIXME return something
- return nil
- }
- func (s *Server) handleAccountPasswordChange(w http.ResponseWriter, r *http.Request, v *visitor) error {
- if v.user == nil {
- return errHTTPUnauthorized
- }
- body, err := util.Peek(r.Body, 4096) // FIXME
- if err != nil {
- return err
- }
- defer r.Body.Close()
- var newPassword apiAccountCreateRequest // Re-use!
- if err := json.NewDecoder(body).Decode(&newPassword); err != nil {
- return err
- }
- if err := s.auth.ChangePassword(v.user.Name, newPassword.Password); err != nil {
- return err
- }
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
- // FIXME return something
- return nil
- }
- func (s *Server) handleAccountTokenGet(w http.ResponseWriter, r *http.Request, v *visitor) error {
- // TODO rate limit
- if v.user == nil {
- return errHTTPUnauthorized
- }
- token, err := s.auth.CreateToken(v.user)
- if err != nil {
- return err
- }
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
- response := &apiAccountTokenResponse{
- Token: token,
- }
- if err := json.NewEncoder(w).Encode(response); err != nil {
- return err
- }
- return nil
- }
- func (s *Server) handleAccountTokenDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
- // TODO rate limit
- if v.user == nil || v.user.Token == "" {
- return errHTTPUnauthorized
- }
- if err := s.auth.RemoveToken(v.user); err != nil {
- return err
- }
- w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
- return nil
- }
- func (s *Server) handleAccountSettingsChange(w http.ResponseWriter, r *http.Request, v *visitor) error {
- if v.user == nil {
- return errors.New("no user")
- }
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
- body, err := util.Peek(r.Body, 4096) // FIXME
- if err != nil {
- return err
- }
- defer r.Body.Close()
- var newPrefs auth.UserPrefs
- if err := json.NewDecoder(body).Decode(&newPrefs); err != nil {
- return err
- }
- if v.user.Prefs == nil {
- v.user.Prefs = &auth.UserPrefs{}
- }
- prefs := v.user.Prefs
- if newPrefs.Language != "" {
- prefs.Language = newPrefs.Language
- }
- if newPrefs.Notification != nil {
- if prefs.Notification == nil {
- prefs.Notification = &auth.UserNotificationPrefs{}
- }
- if newPrefs.Notification.DeleteAfter > 0 {
- prefs.Notification.DeleteAfter = newPrefs.Notification.DeleteAfter
- }
- if newPrefs.Notification.Sound != "" {
- prefs.Notification.Sound = newPrefs.Notification.Sound
- }
- if newPrefs.Notification.MinPriority > 0 {
- prefs.Notification.MinPriority = newPrefs.Notification.MinPriority
- }
- }
- return s.auth.ChangeSettings(v.user)
- }
- func (s *Server) handleAccountSubscriptionAdd(w http.ResponseWriter, r *http.Request, v *visitor) error {
- if v.user == nil {
- return errors.New("no user")
- }
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
- body, err := util.Peek(r.Body, 4096) // FIXME
- if err != nil {
- return err
- }
- defer r.Body.Close()
- var newSubscription auth.UserSubscription
- if err := json.NewDecoder(body).Decode(&newSubscription); err != nil {
- return err
- }
- if v.user.Prefs == nil {
- v.user.Prefs = &auth.UserPrefs{}
- }
- newSubscription.ID = "" // Client cannot set ID
- for _, subscription := range v.user.Prefs.Subscriptions {
- if newSubscription.BaseURL == subscription.BaseURL && newSubscription.Topic == subscription.Topic {
- newSubscription = *subscription
- break
- }
- }
- if newSubscription.ID == "" {
- newSubscription.ID = util.RandomString(16)
- v.user.Prefs.Subscriptions = append(v.user.Prefs.Subscriptions, &newSubscription)
- if err := s.auth.ChangeSettings(v.user); err != nil {
- return err
- }
- }
- if err := json.NewEncoder(w).Encode(newSubscription); err != nil {
- return err
- }
- return nil
- }
- func (s *Server) handleAccountSubscriptionDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
- if v.user == nil {
- return errors.New("no user")
- }
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
- matches := accountSubscriptionSingleRegex.FindStringSubmatch(r.URL.Path)
- if len(matches) != 2 {
- return errHTTPInternalErrorInvalidFilePath // FIXME
- }
- subscriptionID := matches[1]
- if v.user.Prefs == nil || v.user.Prefs.Subscriptions == nil {
- return nil
- }
- newSubscriptions := make([]*auth.UserSubscription, 0)
- for _, subscription := range v.user.Prefs.Subscriptions {
- if subscription.ID != subscriptionID {
- newSubscriptions = append(newSubscriptions, subscription)
- }
- }
- if len(newSubscriptions) < len(v.user.Prefs.Subscriptions) {
- v.user.Prefs.Subscriptions = newSubscriptions
- if err := s.auth.ChangeSettings(v.user); err != nil {
- return err
- }
- }
- return nil
- }
|