visitor.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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. // SetUser sets the visitors user to the given value
  205. func (v *visitor) SetUser(u *user.User) {
  206. v.mu.Lock()
  207. defer v.mu.Unlock()
  208. v.user = u
  209. }
  210. // MaybeUserID returns the user ID of the visitor (if any). If this is an anonymous visitor,
  211. // an empty string is returned.
  212. func (v *visitor) MaybeUserID() string {
  213. v.mu.Lock()
  214. defer v.mu.Unlock()
  215. if v.user != nil {
  216. return v.user.ID
  217. }
  218. return ""
  219. }
  220. func (v *visitor) Limits() *visitorLimits {
  221. v.mu.Lock()
  222. defer v.mu.Unlock()
  223. limits := defaultVisitorLimits(v.config)
  224. if v.user != nil && v.user.Tier != nil {
  225. limits.Basis = visitorLimitBasisTier
  226. limits.MessagesLimit = v.user.Tier.MessagesLimit
  227. limits.MessagesExpiryDuration = v.user.Tier.MessagesExpiryDuration
  228. limits.EmailsLimit = v.user.Tier.EmailsLimit
  229. limits.ReservationsLimit = v.user.Tier.ReservationsLimit
  230. limits.AttachmentTotalSizeLimit = v.user.Tier.AttachmentTotalSizeLimit
  231. limits.AttachmentFileSizeLimit = v.user.Tier.AttachmentFileSizeLimit
  232. limits.AttachmentExpiryDuration = v.user.Tier.AttachmentExpiryDuration
  233. }
  234. return limits
  235. }
  236. func (v *visitor) Info() (*visitorInfo, error) {
  237. v.mu.Lock()
  238. messages := v.messages
  239. emails := v.emails
  240. v.mu.Unlock()
  241. var attachmentsBytesUsed int64
  242. var err error
  243. if v.user != nil {
  244. attachmentsBytesUsed, err = v.messageCache.AttachmentBytesUsedByUser(v.user.ID)
  245. } else {
  246. attachmentsBytesUsed, err = v.messageCache.AttachmentBytesUsedBySender(v.ip.String())
  247. }
  248. if err != nil {
  249. return nil, err
  250. }
  251. var reservations int64
  252. if v.user != nil && v.userManager != nil {
  253. reservations, err = v.userManager.ReservationsCount(v.user.Name)
  254. if err != nil {
  255. return nil, err
  256. }
  257. }
  258. limits := v.Limits()
  259. stats := &visitorStats{
  260. Messages: messages,
  261. MessagesRemaining: zeroIfNegative(limits.MessagesLimit - messages),
  262. Emails: emails,
  263. EmailsRemaining: zeroIfNegative(limits.EmailsLimit - emails),
  264. Reservations: reservations,
  265. ReservationsRemaining: zeroIfNegative(limits.ReservationsLimit - reservations),
  266. AttachmentTotalSize: attachmentsBytesUsed,
  267. AttachmentTotalSizeRemaining: zeroIfNegative(limits.AttachmentTotalSizeLimit - attachmentsBytesUsed),
  268. }
  269. return &visitorInfo{
  270. Limits: limits,
  271. Stats: stats,
  272. }, nil
  273. }
  274. func zeroIfNegative(value int64) int64 {
  275. if value < 0 {
  276. return 0
  277. }
  278. return value
  279. }
  280. func replenishDurationToDailyLimit(duration time.Duration) int64 {
  281. return int64(24 * time.Hour / duration)
  282. }
  283. func dailyLimitToRate(limit int64) rate.Limit {
  284. return rate.Limit(limit) * rate.Every(24*time.Hour)
  285. }
  286. func defaultVisitorLimits(conf *Config) *visitorLimits {
  287. return &visitorLimits{
  288. Basis: visitorLimitBasisIP,
  289. MessagesLimit: replenishDurationToDailyLimit(conf.VisitorRequestLimitReplenish),
  290. MessagesExpiryDuration: conf.CacheDuration,
  291. EmailsLimit: replenishDurationToDailyLimit(conf.VisitorEmailLimitReplenish),
  292. ReservationsLimit: visitorDefaultReservationsLimit,
  293. AttachmentTotalSizeLimit: conf.VisitorAttachmentTotalSizeLimit,
  294. AttachmentFileSizeLimit: conf.AttachmentFileSizeLimit,
  295. AttachmentExpiryDuration: conf.AttachmentExpiryDuration,
  296. }
  297. }