visitor.go 9.1 KB

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