visitor.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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 // Last seen time of this visitor (needed for removal of stale visitors)
  37. mu sync.Mutex
  38. }
  39. type visitorInfo struct {
  40. Limits *visitorLimits
  41. Stats *visitorStats
  42. }
  43. type visitorLimits struct {
  44. Basis visitorLimitBasis
  45. MessagesLimit int64
  46. MessagesExpiryDuration time.Duration
  47. EmailsLimit int64
  48. ReservationsLimit int64
  49. AttachmentTotalSizeLimit int64
  50. AttachmentFileSizeLimit int64
  51. AttachmentExpiryDuration time.Duration
  52. }
  53. type visitorStats struct {
  54. Messages int64
  55. MessagesRemaining int64
  56. Emails int64
  57. EmailsRemaining int64
  58. Reservations int64
  59. ReservationsRemaining int64
  60. AttachmentTotalSize int64
  61. AttachmentTotalSizeRemaining int64
  62. }
  63. // visitorLimitBasis describes how the visitor limits were derived, either from a user's
  64. // IP address (default config), or from its tier
  65. type visitorLimitBasis string
  66. const (
  67. visitorLimitBasisIP = visitorLimitBasis("ip")
  68. visitorLimitBasisTier = visitorLimitBasis("tier")
  69. )
  70. func newVisitor(conf *Config, messageCache *messageCache, userManager *user.Manager, ip netip.Addr, user *user.User) *visitor {
  71. var messagesLimiter util.Limiter
  72. var requestLimiter, emailsLimiter, accountLimiter *rate.Limiter
  73. var messages, emails int64
  74. if user != nil {
  75. messages = user.Stats.Messages
  76. emails = user.Stats.Emails
  77. } else {
  78. accountLimiter = rate.NewLimiter(rate.Every(conf.VisitorAccountCreateLimitReplenish), conf.VisitorAccountCreateLimitBurst)
  79. }
  80. if user != nil && user.Tier != nil {
  81. requestLimiter = rate.NewLimiter(dailyLimitToRate(user.Tier.MessagesLimit), conf.VisitorRequestLimitBurst)
  82. messagesLimiter = util.NewFixedLimiter(user.Tier.MessagesLimit)
  83. emailsLimiter = rate.NewLimiter(dailyLimitToRate(user.Tier.EmailsLimit), conf.VisitorEmailLimitBurst)
  84. } else {
  85. requestLimiter = rate.NewLimiter(rate.Every(conf.VisitorRequestLimitReplenish), conf.VisitorRequestLimitBurst)
  86. emailsLimiter = rate.NewLimiter(rate.Every(conf.VisitorEmailLimitReplenish), conf.VisitorEmailLimitBurst)
  87. }
  88. return &visitor{
  89. config: conf,
  90. messageCache: messageCache,
  91. userManager: userManager, // May be nil
  92. ip: ip,
  93. user: user,
  94. messages: messages,
  95. emails: emails,
  96. requestLimiter: requestLimiter,
  97. messagesLimiter: messagesLimiter, // May be nil
  98. emailsLimiter: emailsLimiter,
  99. subscriptionLimiter: util.NewFixedLimiter(int64(conf.VisitorSubscriptionLimit)),
  100. bandwidthLimiter: util.NewBytesLimiter(conf.VisitorAttachmentDailyBandwidthLimit, 24*time.Hour),
  101. accountLimiter: accountLimiter, // May be nil
  102. firebase: time.Unix(0, 0),
  103. seen: time.Now(),
  104. }
  105. }
  106. func (v *visitor) RequestAllowed() error {
  107. if !v.requestLimiter.Allow() {
  108. return errVisitorLimitReached
  109. }
  110. return nil
  111. }
  112. func (v *visitor) FirebaseAllowed() error {
  113. v.mu.Lock()
  114. defer v.mu.Unlock()
  115. if time.Now().Before(v.firebase) {
  116. return errVisitorLimitReached
  117. }
  118. return nil
  119. }
  120. func (v *visitor) FirebaseTemporarilyDeny() {
  121. v.mu.Lock()
  122. defer v.mu.Unlock()
  123. v.firebase = time.Now().Add(v.config.FirebaseQuotaExceededPenaltyDuration)
  124. }
  125. func (v *visitor) MessageAllowed() error {
  126. if v.messagesLimiter != nil && v.messagesLimiter.Allow(1) != nil {
  127. return errVisitorLimitReached
  128. }
  129. return nil
  130. }
  131. func (v *visitor) EmailAllowed() error {
  132. if !v.emailsLimiter.Allow() {
  133. return errVisitorLimitReached
  134. }
  135. return nil
  136. }
  137. func (v *visitor) SubscriptionAllowed() error {
  138. v.mu.Lock()
  139. defer v.mu.Unlock()
  140. if err := v.subscriptionLimiter.Allow(1); err != nil {
  141. return errVisitorLimitReached
  142. }
  143. return nil
  144. }
  145. func (v *visitor) RemoveSubscription() {
  146. v.mu.Lock()
  147. defer v.mu.Unlock()
  148. v.subscriptionLimiter.Allow(-1)
  149. }
  150. func (v *visitor) Keepalive() {
  151. v.mu.Lock()
  152. defer v.mu.Unlock()
  153. v.seen = time.Now()
  154. }
  155. func (v *visitor) BandwidthLimiter() util.Limiter {
  156. return v.bandwidthLimiter
  157. }
  158. func (v *visitor) Stale() bool {
  159. v.mu.Lock()
  160. defer v.mu.Unlock()
  161. return time.Since(v.seen) > visitorExpungeAfter
  162. }
  163. func (v *visitor) IncrementMessages() {
  164. v.mu.Lock()
  165. defer v.mu.Unlock()
  166. v.messages++
  167. if v.user != nil {
  168. v.user.Stats.Messages = v.messages
  169. }
  170. }
  171. func (v *visitor) IncrementEmails() {
  172. v.mu.Lock()
  173. defer v.mu.Unlock()
  174. v.emails++
  175. if v.user != nil {
  176. v.user.Stats.Emails = v.emails
  177. }
  178. }
  179. func (v *visitor) ResetStats() {
  180. v.mu.Lock()
  181. defer v.mu.Unlock()
  182. v.messages = 0
  183. v.emails = 0
  184. if v.user != nil {
  185. v.user.Stats.Messages = 0
  186. v.user.Stats.Emails = 0
  187. // v.messagesLimiter = ... // FIXME
  188. }
  189. }
  190. func (v *visitor) Limits() *visitorLimits {
  191. limits := defaultVisitorLimits(v.config)
  192. if v.user != nil && v.user.Tier != nil {
  193. limits.Basis = visitorLimitBasisTier
  194. limits.MessagesLimit = v.user.Tier.MessagesLimit
  195. limits.MessagesExpiryDuration = v.user.Tier.MessagesExpiryDuration
  196. limits.EmailsLimit = v.user.Tier.EmailsLimit
  197. limits.ReservationsLimit = v.user.Tier.ReservationsLimit
  198. limits.AttachmentTotalSizeLimit = v.user.Tier.AttachmentTotalSizeLimit
  199. limits.AttachmentFileSizeLimit = v.user.Tier.AttachmentFileSizeLimit
  200. limits.AttachmentExpiryDuration = v.user.Tier.AttachmentExpiryDuration
  201. }
  202. return limits
  203. }
  204. func (v *visitor) Info() (*visitorInfo, error) {
  205. v.mu.Lock()
  206. messages := v.messages
  207. emails := v.emails
  208. v.mu.Unlock()
  209. var attachmentsBytesUsed int64
  210. var err error
  211. if v.user != nil {
  212. attachmentsBytesUsed, err = v.messageCache.AttachmentBytesUsedByUser(v.user.Name)
  213. } else {
  214. attachmentsBytesUsed, err = v.messageCache.AttachmentBytesUsedBySender(v.ip.String())
  215. }
  216. if err != nil {
  217. return nil, err
  218. }
  219. var reservations int64
  220. if v.user != nil && v.userManager != nil {
  221. reservations, err = v.userManager.ReservationsCount(v.user.Name)
  222. if err != nil {
  223. return nil, err
  224. }
  225. }
  226. limits := v.Limits()
  227. stats := &visitorStats{
  228. Messages: messages,
  229. MessagesRemaining: zeroIfNegative(limits.MessagesLimit - messages),
  230. Emails: emails,
  231. EmailsRemaining: zeroIfNegative(limits.EmailsLimit - emails),
  232. Reservations: reservations,
  233. ReservationsRemaining: zeroIfNegative(limits.ReservationsLimit - reservations),
  234. AttachmentTotalSize: attachmentsBytesUsed,
  235. AttachmentTotalSizeRemaining: zeroIfNegative(limits.AttachmentTotalSizeLimit - attachmentsBytesUsed),
  236. }
  237. return &visitorInfo{
  238. Limits: limits,
  239. Stats: stats,
  240. }, nil
  241. }
  242. func zeroIfNegative(value int64) int64 {
  243. if value < 0 {
  244. return 0
  245. }
  246. return value
  247. }
  248. func replenishDurationToDailyLimit(duration time.Duration) int64 {
  249. return int64(24 * time.Hour / duration)
  250. }
  251. func dailyLimitToRate(limit int64) rate.Limit {
  252. return rate.Limit(limit) * rate.Every(24*time.Hour)
  253. }
  254. func defaultVisitorLimits(conf *Config) *visitorLimits {
  255. return &visitorLimits{
  256. Basis: visitorLimitBasisIP,
  257. MessagesLimit: replenishDurationToDailyLimit(conf.VisitorRequestLimitReplenish),
  258. MessagesExpiryDuration: conf.CacheDuration,
  259. EmailsLimit: replenishDurationToDailyLimit(conf.VisitorEmailLimitReplenish),
  260. ReservationsLimit: 0, // No reservations for anonymous users, or users without a tier
  261. AttachmentTotalSizeLimit: conf.VisitorAttachmentTotalSizeLimit,
  262. AttachmentFileSizeLimit: conf.AttachmentFileSizeLimit,
  263. AttachmentExpiryDuration: conf.AttachmentExpiryDuration,
  264. }
  265. }