visitor.go 9.5 KB

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