visitor.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. package server
  2. import (
  3. "errors"
  4. "heckel.io/ntfy/auth"
  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. ip netip.Addr
  25. user *auth.User
  26. messages int64 // Number of messages sent
  27. emails int64 // Number of emails sent
  28. requestLimiter *rate.Limiter // Rate limiter for (almost) all requests (including messages)
  29. emailsLimiter *rate.Limiter // Rate limiter for emails
  30. subscriptionLimiter util.Limiter // Fixed limiter for active subscriptions (ongoing connections)
  31. bandwidthLimiter util.Limiter
  32. accountLimiter *rate.Limiter // Rate limiter for account creation
  33. firebase time.Time // Next allowed Firebase message
  34. seen time.Time
  35. mu sync.Mutex
  36. }
  37. type visitorStats struct {
  38. Basis string // "ip", "role" or "plan"
  39. Messages int64
  40. MessagesLimit int64
  41. MessagesRemaining int64
  42. Emails int64
  43. EmailsLimit int64
  44. EmailsRemaining int64
  45. AttachmentTotalSize int64
  46. AttachmentTotalSizeLimit int64
  47. AttachmentTotalSizeRemaining int64
  48. AttachmentFileSizeLimit int64
  49. }
  50. func newVisitor(conf *Config, messageCache *messageCache, ip netip.Addr, user *auth.User) *visitor {
  51. var requestLimiter, emailsLimiter, accountLimiter *rate.Limiter
  52. var messages, emails int64
  53. if user != nil {
  54. messages = user.Stats.Messages
  55. emails = user.Stats.Emails
  56. } else {
  57. accountLimiter = rate.NewLimiter(rate.Every(conf.VisitorAccountCreateLimitReplenish), conf.VisitorAccountCreateLimitBurst)
  58. }
  59. if user != nil && user.Plan != nil {
  60. requestLimiter = rate.NewLimiter(dailyLimitToRate(user.Plan.MessagesLimit), conf.VisitorRequestLimitBurst)
  61. emailsLimiter = rate.NewLimiter(dailyLimitToRate(user.Plan.EmailsLimit), conf.VisitorEmailLimitBurst)
  62. } else {
  63. requestLimiter = rate.NewLimiter(rate.Every(conf.VisitorRequestLimitReplenish), conf.VisitorRequestLimitBurst)
  64. emailsLimiter = rate.NewLimiter(rate.Every(conf.VisitorEmailLimitReplenish), conf.VisitorEmailLimitBurst)
  65. }
  66. return &visitor{
  67. config: conf,
  68. messageCache: messageCache,
  69. ip: ip,
  70. user: user,
  71. messages: messages,
  72. emails: emails,
  73. requestLimiter: requestLimiter,
  74. emailsLimiter: emailsLimiter,
  75. subscriptionLimiter: util.NewFixedLimiter(int64(conf.VisitorSubscriptionLimit)),
  76. bandwidthLimiter: util.NewBytesLimiter(conf.VisitorAttachmentDailyBandwidthLimit, 24*time.Hour),
  77. accountLimiter: accountLimiter, // May be nil
  78. firebase: time.Unix(0, 0),
  79. seen: time.Now(),
  80. }
  81. }
  82. func (v *visitor) RequestAllowed() error {
  83. if !v.requestLimiter.Allow() {
  84. return errVisitorLimitReached
  85. }
  86. return nil
  87. }
  88. func (v *visitor) FirebaseAllowed() error {
  89. v.mu.Lock()
  90. defer v.mu.Unlock()
  91. if time.Now().Before(v.firebase) {
  92. return errVisitorLimitReached
  93. }
  94. return nil
  95. }
  96. func (v *visitor) FirebaseTemporarilyDeny() {
  97. v.mu.Lock()
  98. defer v.mu.Unlock()
  99. v.firebase = time.Now().Add(v.config.FirebaseQuotaExceededPenaltyDuration)
  100. }
  101. func (v *visitor) EmailAllowed() error {
  102. if !v.emailsLimiter.Allow() {
  103. return errVisitorLimitReached
  104. }
  105. return nil
  106. }
  107. func (v *visitor) SubscriptionAllowed() error {
  108. v.mu.Lock()
  109. defer v.mu.Unlock()
  110. if err := v.subscriptionLimiter.Allow(1); err != nil {
  111. return errVisitorLimitReached
  112. }
  113. return nil
  114. }
  115. func (v *visitor) RemoveSubscription() {
  116. v.mu.Lock()
  117. defer v.mu.Unlock()
  118. v.subscriptionLimiter.Allow(-1)
  119. }
  120. func (v *visitor) Keepalive() {
  121. v.mu.Lock()
  122. defer v.mu.Unlock()
  123. v.seen = time.Now()
  124. }
  125. func (v *visitor) BandwidthLimiter() util.Limiter {
  126. return v.bandwidthLimiter
  127. }
  128. func (v *visitor) Stale() bool {
  129. v.mu.Lock()
  130. defer v.mu.Unlock()
  131. return time.Since(v.seen) > visitorExpungeAfter
  132. }
  133. func (v *visitor) IncrMessages() {
  134. v.mu.Lock()
  135. defer v.mu.Unlock()
  136. v.messages++
  137. if v.user != nil {
  138. v.user.Stats.Messages = v.messages
  139. }
  140. }
  141. func (v *visitor) IncrEmails() {
  142. v.mu.Lock()
  143. defer v.mu.Unlock()
  144. v.emails++
  145. if v.user != nil {
  146. v.user.Stats.Emails = v.emails
  147. }
  148. }
  149. func (v *visitor) Stats() (*visitorStats, error) {
  150. v.mu.Lock()
  151. messages := v.messages
  152. emails := v.emails
  153. v.mu.Unlock()
  154. stats := &visitorStats{}
  155. if v.user != nil && v.user.Role == auth.RoleAdmin {
  156. stats.Basis = "role"
  157. stats.MessagesLimit = 0
  158. stats.EmailsLimit = 0
  159. stats.AttachmentTotalSizeLimit = 0
  160. stats.AttachmentFileSizeLimit = 0
  161. } else if v.user != nil && v.user.Plan != nil {
  162. stats.Basis = "plan"
  163. stats.MessagesLimit = v.user.Plan.MessagesLimit
  164. stats.EmailsLimit = v.user.Plan.EmailsLimit
  165. stats.AttachmentTotalSizeLimit = v.user.Plan.AttachmentTotalSizeLimit
  166. stats.AttachmentFileSizeLimit = v.user.Plan.AttachmentFileSizeLimit
  167. } else {
  168. stats.Basis = "ip"
  169. stats.MessagesLimit = replenishDurationToDailyLimit(v.config.VisitorRequestLimitReplenish)
  170. stats.EmailsLimit = replenishDurationToDailyLimit(v.config.VisitorEmailLimitReplenish)
  171. stats.AttachmentTotalSizeLimit = v.config.VisitorAttachmentTotalSizeLimit
  172. stats.AttachmentFileSizeLimit = v.config.AttachmentFileSizeLimit
  173. }
  174. var attachmentsBytesUsed int64
  175. var err error
  176. if v.user != nil {
  177. attachmentsBytesUsed, err = v.messageCache.AttachmentBytesUsedByUser(v.user.Name)
  178. } else {
  179. attachmentsBytesUsed, err = v.messageCache.AttachmentBytesUsedBySender(v.ip.String())
  180. }
  181. if err != nil {
  182. return nil, err
  183. }
  184. stats.Messages = messages
  185. stats.MessagesRemaining = zeroIfNegative(stats.MessagesLimit - stats.Messages)
  186. stats.Emails = emails
  187. stats.EmailsRemaining = zeroIfNegative(stats.EmailsLimit - stats.Emails)
  188. stats.AttachmentTotalSize = attachmentsBytesUsed
  189. stats.AttachmentTotalSizeRemaining = zeroIfNegative(stats.AttachmentTotalSizeLimit - stats.AttachmentTotalSize)
  190. return stats, nil
  191. }
  192. func zeroIfNegative(value int64) int64 {
  193. if value < 0 {
  194. return 0
  195. }
  196. return value
  197. }
  198. func replenishDurationToDailyLimit(duration time.Duration) int64 {
  199. return int64(24 * time.Hour / duration)
  200. }
  201. func dailyLimitToRate(limit int64) rate.Limit {
  202. return rate.Limit(limit) * rate.Every(24*time.Hour)
  203. }