visitor.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. package server
  2. import (
  3. "errors"
  4. "heckel.io/ntfy/user"
  5. "net/netip"
  6. "sync"
  7. "time"
  8. "golang.org/x/time/rate"
  9. "heckel.io/ntfy/util"
  10. )
  11. const (
  12. // visitorExpungeAfter defines how long a visitor is active before it is removed from memory. This number
  13. // has to be very high to prevent e-mail abuse, but it doesn't really affect the other limits anyway, since
  14. // they are replenished faster (typically).
  15. visitorExpungeAfter = 24 * time.Hour
  16. )
  17. var (
  18. errVisitorLimitReached = errors.New("limit reached")
  19. )
  20. // visitor represents an API user, and its associated rate.Limiter used for rate limiting
  21. type visitor struct {
  22. config *Config
  23. messageCache *messageCache
  24. userManager *user.Manager // May be nil!
  25. ip netip.Addr
  26. user *user.User
  27. messages int64 // Number of messages sent, reset every day
  28. emails int64 // Number of emails sent, reset every day
  29. requestLimiter *rate.Limiter // Rate limiter for (almost) all requests (including messages)
  30. messagesLimiter util.Limiter // Rate limiter for messages, may be nil
  31. emailsLimiter *rate.Limiter // Rate limiter for emails
  32. subscriptionLimiter util.Limiter // Fixed limiter for active subscriptions (ongoing connections)
  33. bandwidthLimiter util.Limiter // Limiter for attachment bandwidth downloads
  34. accountLimiter *rate.Limiter // Rate limiter for account creation
  35. firebase time.Time // Next allowed Firebase message
  36. seen time.Time
  37. mu sync.Mutex
  38. }
  39. type visitorInfo struct {
  40. Basis string // "ip", "role" or "tier"
  41. Messages int64
  42. MessagesLimit int64
  43. MessagesRemaining int64
  44. MessagesExpiryDuration int64
  45. Emails int64
  46. EmailsLimit int64
  47. EmailsRemaining int64
  48. Reservations int64
  49. ReservationsLimit int64
  50. ReservationsRemaining int64
  51. AttachmentTotalSize int64
  52. AttachmentTotalSizeLimit int64
  53. AttachmentTotalSizeRemaining int64
  54. AttachmentFileSizeLimit int64
  55. AttachmentExpiryDuration int64
  56. }
  57. func newVisitor(conf *Config, messageCache *messageCache, userManager *user.Manager, ip netip.Addr, user *user.User) *visitor {
  58. var messagesLimiter util.Limiter
  59. var requestLimiter, emailsLimiter, accountLimiter *rate.Limiter
  60. var messages, emails int64
  61. if user != nil {
  62. messages = user.Stats.Messages
  63. emails = user.Stats.Emails
  64. } else {
  65. accountLimiter = rate.NewLimiter(rate.Every(conf.VisitorAccountCreateLimitReplenish), conf.VisitorAccountCreateLimitBurst)
  66. }
  67. if user != nil && user.Tier != nil {
  68. requestLimiter = rate.NewLimiter(dailyLimitToRate(user.Tier.MessagesLimit), conf.VisitorRequestLimitBurst)
  69. messagesLimiter = util.NewFixedLimiter(user.Tier.MessagesLimit)
  70. emailsLimiter = rate.NewLimiter(dailyLimitToRate(user.Tier.EmailsLimit), conf.VisitorEmailLimitBurst)
  71. } else {
  72. requestLimiter = rate.NewLimiter(rate.Every(conf.VisitorRequestLimitReplenish), conf.VisitorRequestLimitBurst)
  73. emailsLimiter = rate.NewLimiter(rate.Every(conf.VisitorEmailLimitReplenish), conf.VisitorEmailLimitBurst)
  74. }
  75. return &visitor{
  76. config: conf,
  77. messageCache: messageCache,
  78. userManager: userManager, // May be nil!
  79. ip: ip,
  80. user: user,
  81. messages: messages,
  82. emails: emails,
  83. requestLimiter: requestLimiter,
  84. messagesLimiter: messagesLimiter,
  85. emailsLimiter: emailsLimiter,
  86. subscriptionLimiter: util.NewFixedLimiter(int64(conf.VisitorSubscriptionLimit)),
  87. bandwidthLimiter: util.NewBytesLimiter(conf.VisitorAttachmentDailyBandwidthLimit, 24*time.Hour),
  88. accountLimiter: accountLimiter, // May be nil
  89. firebase: time.Unix(0, 0),
  90. seen: time.Now(),
  91. }
  92. }
  93. func (v *visitor) RequestAllowed() error {
  94. if !v.requestLimiter.Allow() {
  95. return errVisitorLimitReached
  96. }
  97. return nil
  98. }
  99. func (v *visitor) FirebaseAllowed() error {
  100. v.mu.Lock()
  101. defer v.mu.Unlock()
  102. if time.Now().Before(v.firebase) {
  103. return errVisitorLimitReached
  104. }
  105. return nil
  106. }
  107. func (v *visitor) FirebaseTemporarilyDeny() {
  108. v.mu.Lock()
  109. defer v.mu.Unlock()
  110. v.firebase = time.Now().Add(v.config.FirebaseQuotaExceededPenaltyDuration)
  111. }
  112. func (v *visitor) MessageAllowed() error {
  113. if v.messagesLimiter != nil && v.messagesLimiter.Allow(1) != nil {
  114. return errVisitorLimitReached
  115. }
  116. return nil
  117. }
  118. func (v *visitor) EmailAllowed() error {
  119. if !v.emailsLimiter.Allow() {
  120. return errVisitorLimitReached
  121. }
  122. return nil
  123. }
  124. func (v *visitor) SubscriptionAllowed() error {
  125. v.mu.Lock()
  126. defer v.mu.Unlock()
  127. if err := v.subscriptionLimiter.Allow(1); err != nil {
  128. return errVisitorLimitReached
  129. }
  130. return nil
  131. }
  132. func (v *visitor) RemoveSubscription() {
  133. v.mu.Lock()
  134. defer v.mu.Unlock()
  135. v.subscriptionLimiter.Allow(-1)
  136. }
  137. func (v *visitor) Keepalive() {
  138. v.mu.Lock()
  139. defer v.mu.Unlock()
  140. v.seen = time.Now()
  141. }
  142. func (v *visitor) BandwidthLimiter() util.Limiter {
  143. return v.bandwidthLimiter
  144. }
  145. func (v *visitor) Stale() bool {
  146. v.mu.Lock()
  147. defer v.mu.Unlock()
  148. return time.Since(v.seen) > visitorExpungeAfter
  149. }
  150. func (v *visitor) IncrMessages() {
  151. v.mu.Lock()
  152. defer v.mu.Unlock()
  153. v.messages++
  154. if v.user != nil {
  155. v.user.Stats.Messages = v.messages
  156. }
  157. }
  158. func (v *visitor) IncrEmails() {
  159. v.mu.Lock()
  160. defer v.mu.Unlock()
  161. v.emails++
  162. if v.user != nil {
  163. v.user.Stats.Emails = v.emails
  164. }
  165. }
  166. func (v *visitor) Info() (*visitorInfo, error) {
  167. v.mu.Lock()
  168. messages := v.messages
  169. emails := v.emails
  170. v.mu.Unlock()
  171. info := &visitorInfo{}
  172. if v.user != nil && v.user.Role == user.RoleAdmin {
  173. info.Basis = "role"
  174. // All limits are zero!
  175. info.MessagesExpiryDuration = 24 * 3600 // FIXME this is awful. Should be from the Unlimited plan
  176. info.AttachmentExpiryDuration = 24 * 3600 // FIXME this is awful. Should be from the Unlimited plan
  177. } else if v.user != nil && v.user.Tier != nil {
  178. info.Basis = "tier"
  179. info.MessagesLimit = v.user.Tier.MessagesLimit
  180. info.MessagesExpiryDuration = v.user.Tier.MessagesExpiryDuration
  181. info.EmailsLimit = v.user.Tier.EmailsLimit
  182. info.ReservationsLimit = v.user.Tier.ReservationsLimit
  183. info.AttachmentTotalSizeLimit = v.user.Tier.AttachmentTotalSizeLimit
  184. info.AttachmentFileSizeLimit = v.user.Tier.AttachmentFileSizeLimit
  185. info.AttachmentExpiryDuration = v.user.Tier.AttachmentExpiryDuration
  186. } else {
  187. info.Basis = "ip"
  188. info.MessagesLimit = replenishDurationToDailyLimit(v.config.VisitorRequestLimitReplenish)
  189. info.MessagesExpiryDuration = int64(v.config.CacheDuration.Seconds())
  190. info.EmailsLimit = replenishDurationToDailyLimit(v.config.VisitorEmailLimitReplenish)
  191. info.ReservationsLimit = 0 // FIXME
  192. info.AttachmentTotalSizeLimit = v.config.VisitorAttachmentTotalSizeLimit
  193. info.AttachmentFileSizeLimit = v.config.AttachmentFileSizeLimit
  194. info.AttachmentExpiryDuration = int64(v.config.AttachmentExpiryDuration.Seconds())
  195. }
  196. var attachmentsBytesUsed int64 // FIXME Maybe move this to endpoint?
  197. var err error
  198. if v.user != nil {
  199. attachmentsBytesUsed, err = v.messageCache.AttachmentBytesUsedByUser(v.user.Name)
  200. } else {
  201. attachmentsBytesUsed, err = v.messageCache.AttachmentBytesUsedBySender(v.ip.String())
  202. }
  203. if err != nil {
  204. return nil, err
  205. }
  206. var reservations int64
  207. if v.user != nil && v.userManager != nil {
  208. reservations, err = v.userManager.ReservationsCount(v.user.Name) // FIXME dup call, move this to endpoint?
  209. if err != nil {
  210. return nil, err
  211. }
  212. }
  213. info.Messages = messages
  214. info.MessagesRemaining = zeroIfNegative(info.MessagesLimit - info.Messages)
  215. info.Emails = emails
  216. info.EmailsRemaining = zeroIfNegative(info.EmailsLimit - info.Emails)
  217. info.Reservations = reservations
  218. info.ReservationsRemaining = zeroIfNegative(info.ReservationsLimit - info.Reservations)
  219. info.AttachmentTotalSize = attachmentsBytesUsed
  220. info.AttachmentTotalSizeRemaining = zeroIfNegative(info.AttachmentTotalSizeLimit - info.AttachmentTotalSize)
  221. return info, nil
  222. }
  223. func zeroIfNegative(value int64) int64 {
  224. if value < 0 {
  225. return 0
  226. }
  227. return value
  228. }
  229. func replenishDurationToDailyLimit(duration time.Duration) int64 {
  230. return int64(24 * time.Hour / duration)
  231. }
  232. func dailyLimitToRate(limit int64) rate.Limit {
  233. return rate.Limit(limit) * rate.Every(24*time.Hour)
  234. }