server_account.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. package server
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "heckel.io/ntfy/auth"
  6. "heckel.io/ntfy/util"
  7. "net/http"
  8. )
  9. func (s *Server) handleAccountCreate(w http.ResponseWriter, r *http.Request, v *visitor) error {
  10. admin := v.user != nil && v.user.Role == auth.RoleAdmin
  11. if !admin {
  12. if !s.config.EnableSignup {
  13. return errHTTPBadRequestSignupNotEnabled
  14. } else if v.user != nil {
  15. return errHTTPUnauthorized // Cannot create account from user context
  16. }
  17. }
  18. body, err := util.Peek(r.Body, 4096) // FIXME
  19. if err != nil {
  20. return err
  21. }
  22. defer r.Body.Close()
  23. var newAccount apiAccountCreateRequest
  24. if err := json.NewDecoder(body).Decode(&newAccount); err != nil {
  25. return err
  26. }
  27. if existingUser, _ := s.auth.User(newAccount.Username); existingUser != nil {
  28. return errHTTPConflictUserExists
  29. }
  30. if v.accountLimiter != nil && !v.accountLimiter.Allow() {
  31. return errHTTPTooManyRequestsAccountCreateLimit
  32. }
  33. if err := s.auth.AddUser(newAccount.Username, newAccount.Password, auth.RoleUser); err != nil { // TODO this should return a User
  34. return err
  35. }
  36. w.Header().Set("Content-Type", "application/json")
  37. w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
  38. // FIXME return something
  39. return nil
  40. }
  41. func (s *Server) handleAccountGet(w http.ResponseWriter, r *http.Request, v *visitor) error {
  42. w.Header().Set("Content-Type", "application/json")
  43. w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
  44. stats, err := v.Stats()
  45. if err != nil {
  46. return err
  47. }
  48. response := &apiAccountSettingsResponse{
  49. Stats: &apiAccountStats{
  50. Messages: stats.Messages,
  51. MessagesRemaining: stats.MessagesRemaining,
  52. Emails: stats.Emails,
  53. EmailsRemaining: stats.EmailsRemaining,
  54. AttachmentTotalSize: stats.AttachmentTotalSize,
  55. AttachmentTotalSizeRemaining: stats.AttachmentTotalSizeRemaining,
  56. },
  57. Limits: &apiAccountLimits{
  58. Basis: stats.Basis,
  59. Messages: stats.MessagesLimit,
  60. Emails: stats.EmailsLimit,
  61. AttachmentTotalSize: stats.AttachmentTotalSizeLimit,
  62. AttachmentFileSize: stats.AttachmentFileSizeLimit,
  63. },
  64. }
  65. if v.user != nil {
  66. response.Username = v.user.Name
  67. response.Role = string(v.user.Role)
  68. if v.user.Prefs != nil {
  69. if v.user.Prefs.Language != "" {
  70. response.Language = v.user.Prefs.Language
  71. }
  72. if v.user.Prefs.Notification != nil {
  73. response.Notification = v.user.Prefs.Notification
  74. }
  75. if v.user.Prefs.Subscriptions != nil {
  76. response.Subscriptions = v.user.Prefs.Subscriptions
  77. }
  78. }
  79. if v.user.Plan != nil {
  80. response.Plan = &apiAccountPlan{
  81. Code: v.user.Plan.Code,
  82. Upgradable: v.user.Plan.Upgradable,
  83. }
  84. } else if v.user.Role == auth.RoleAdmin {
  85. response.Plan = &apiAccountPlan{
  86. Code: string(auth.PlanUnlimited),
  87. Upgradable: false,
  88. }
  89. } else {
  90. response.Plan = &apiAccountPlan{
  91. Code: string(auth.PlanDefault),
  92. Upgradable: true,
  93. }
  94. }
  95. } else {
  96. response.Username = auth.Everyone
  97. response.Role = string(auth.RoleAnonymous)
  98. response.Plan = &apiAccountPlan{
  99. Code: string(auth.PlanNone),
  100. Upgradable: true,
  101. }
  102. }
  103. if err := json.NewEncoder(w).Encode(response); err != nil {
  104. return err
  105. }
  106. return nil
  107. }
  108. func (s *Server) handleAccountDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
  109. if v.user == nil {
  110. return errHTTPUnauthorized
  111. }
  112. if err := s.auth.RemoveUser(v.user.Name); err != nil {
  113. return err
  114. }
  115. w.Header().Set("Content-Type", "application/json")
  116. w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
  117. // FIXME return something
  118. return nil
  119. }
  120. func (s *Server) handleAccountPasswordChange(w http.ResponseWriter, r *http.Request, v *visitor) error {
  121. if v.user == nil {
  122. return errHTTPUnauthorized
  123. }
  124. body, err := util.Peek(r.Body, 4096) // FIXME
  125. if err != nil {
  126. return err
  127. }
  128. defer r.Body.Close()
  129. var newPassword apiAccountCreateRequest // Re-use!
  130. if err := json.NewDecoder(body).Decode(&newPassword); err != nil {
  131. return err
  132. }
  133. if err := s.auth.ChangePassword(v.user.Name, newPassword.Password); err != nil {
  134. return err
  135. }
  136. w.Header().Set("Content-Type", "application/json")
  137. w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
  138. // FIXME return something
  139. return nil
  140. }
  141. func (s *Server) handleAccountTokenGet(w http.ResponseWriter, r *http.Request, v *visitor) error {
  142. // TODO rate limit
  143. if v.user == nil {
  144. return errHTTPUnauthorized
  145. }
  146. token, err := s.auth.CreateToken(v.user)
  147. if err != nil {
  148. return err
  149. }
  150. w.Header().Set("Content-Type", "application/json")
  151. w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
  152. response := &apiAccountTokenResponse{
  153. Token: token,
  154. }
  155. if err := json.NewEncoder(w).Encode(response); err != nil {
  156. return err
  157. }
  158. return nil
  159. }
  160. func (s *Server) handleAccountTokenDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
  161. // TODO rate limit
  162. if v.user == nil || v.user.Token == "" {
  163. return errHTTPUnauthorized
  164. }
  165. if err := s.auth.RemoveToken(v.user); err != nil {
  166. return err
  167. }
  168. w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
  169. return nil
  170. }
  171. func (s *Server) handleAccountSettingsChange(w http.ResponseWriter, r *http.Request, v *visitor) error {
  172. if v.user == nil {
  173. return errors.New("no user")
  174. }
  175. w.Header().Set("Content-Type", "application/json")
  176. w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
  177. body, err := util.Peek(r.Body, 4096) // FIXME
  178. if err != nil {
  179. return err
  180. }
  181. defer r.Body.Close()
  182. var newPrefs auth.UserPrefs
  183. if err := json.NewDecoder(body).Decode(&newPrefs); err != nil {
  184. return err
  185. }
  186. if v.user.Prefs == nil {
  187. v.user.Prefs = &auth.UserPrefs{}
  188. }
  189. prefs := v.user.Prefs
  190. if newPrefs.Language != "" {
  191. prefs.Language = newPrefs.Language
  192. }
  193. if newPrefs.Notification != nil {
  194. if prefs.Notification == nil {
  195. prefs.Notification = &auth.UserNotificationPrefs{}
  196. }
  197. if newPrefs.Notification.DeleteAfter > 0 {
  198. prefs.Notification.DeleteAfter = newPrefs.Notification.DeleteAfter
  199. }
  200. if newPrefs.Notification.Sound != "" {
  201. prefs.Notification.Sound = newPrefs.Notification.Sound
  202. }
  203. if newPrefs.Notification.MinPriority > 0 {
  204. prefs.Notification.MinPriority = newPrefs.Notification.MinPriority
  205. }
  206. }
  207. return s.auth.ChangeSettings(v.user)
  208. }
  209. func (s *Server) handleAccountSubscriptionAdd(w http.ResponseWriter, r *http.Request, v *visitor) error {
  210. if v.user == nil {
  211. return errors.New("no user")
  212. }
  213. w.Header().Set("Content-Type", "application/json")
  214. w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
  215. body, err := util.Peek(r.Body, 4096) // FIXME
  216. if err != nil {
  217. return err
  218. }
  219. defer r.Body.Close()
  220. var newSubscription auth.UserSubscription
  221. if err := json.NewDecoder(body).Decode(&newSubscription); err != nil {
  222. return err
  223. }
  224. if v.user.Prefs == nil {
  225. v.user.Prefs = &auth.UserPrefs{}
  226. }
  227. newSubscription.ID = "" // Client cannot set ID
  228. for _, subscription := range v.user.Prefs.Subscriptions {
  229. if newSubscription.BaseURL == subscription.BaseURL && newSubscription.Topic == subscription.Topic {
  230. newSubscription = *subscription
  231. break
  232. }
  233. }
  234. if newSubscription.ID == "" {
  235. newSubscription.ID = util.RandomString(16)
  236. v.user.Prefs.Subscriptions = append(v.user.Prefs.Subscriptions, &newSubscription)
  237. if err := s.auth.ChangeSettings(v.user); err != nil {
  238. return err
  239. }
  240. }
  241. if err := json.NewEncoder(w).Encode(newSubscription); err != nil {
  242. return err
  243. }
  244. return nil
  245. }
  246. func (s *Server) handleAccountSubscriptionDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
  247. if v.user == nil {
  248. return errors.New("no user")
  249. }
  250. w.Header().Set("Content-Type", "application/json")
  251. w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
  252. matches := accountSubscriptionSingleRegex.FindStringSubmatch(r.URL.Path)
  253. if len(matches) != 2 {
  254. return errHTTPInternalErrorInvalidFilePath // FIXME
  255. }
  256. subscriptionID := matches[1]
  257. if v.user.Prefs == nil || v.user.Prefs.Subscriptions == nil {
  258. return nil
  259. }
  260. newSubscriptions := make([]*auth.UserSubscription, 0)
  261. for _, subscription := range v.user.Prefs.Subscriptions {
  262. if subscription.ID != subscriptionID {
  263. newSubscriptions = append(newSubscriptions, subscription)
  264. }
  265. }
  266. if len(newSubscriptions) < len(v.user.Prefs.Subscriptions) {
  267. v.user.Prefs.Subscriptions = newSubscriptions
  268. if err := s.auth.ChangeSettings(v.user); err != nil {
  269. return err
  270. }
  271. }
  272. return nil
  273. }