From ae5110ad8382470ef3ab36c547ccc42ce0459bd0 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Wed, 31 Aug 2016 20:36:34 +0200 Subject: [PATCH] rate limit web/mobile user games pagination --- app/controllers/User.scala | 37 ++++++++++++++----- .../common/src/main/paginator/Paginator.scala | 2 + 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/app/controllers/User.scala b/app/controllers/User.scala index 2d0e081d32..49e18b23c9 100644 --- a/app/controllers/User.scala +++ b/app/controllers/User.scala @@ -2,12 +2,15 @@ package controllers import play.api.libs.json._ import play.api.mvc._, Results._ +import scala.concurrent.duration._ import lila.api.{ Context, BodyContext } import lila.app._ import lila.app.mashup.GameFilterMenu +import lila.common.HTTPRequest +import lila.common.paginator.Paginator import lila.evaluation.{ PlayerAggregateAssessment } -import lila.game.{ GameRepo, Pov } +import lila.game.{ GameRepo, Pov, Game => GameModel } import lila.rating.PerfType import lila.user.{ User => UserModel, UserRepo } import views._ @@ -118,19 +121,33 @@ object User extends LilaController { searchForm = GameFilterMenu.searchForm(userGameSearch, filters.current)(ctx.body) } yield html.user.show(u, info, pag, filters, searchForm, relation, notes, followable, blocked) - private def userGames(u: UserModel, filterOption: Option[String], page: Int)(implicit ctx: BodyContext[_]) = { + private val UserGamesRateLimitPerIP = new lila.memo.RateLimit( + credits = 500, + duration = 10 minutes, + name = "user games web/mobile per IP") + + implicit val userGamesDefault = + ornicar.scalalib.Zero.instance[Fu[Paginator[GameModel]]](fuccess(Paginator.empty[GameModel])) + + private def userGames( + u: UserModel, + filterOption: Option[String], + page: Int)(implicit ctx: BodyContext[_]): Fu[(String, Paginator[GameModel])] = { import lila.app.mashup.GameFilter.{ All, Playing } filterOption.fold({ Env.simul isHosting u.id map (_.fold(Playing, All).name) })(fuccess) flatMap { filterName => - GameFilterMenu.paginatorOf( - userGameSearch = userGameSearch, - user = u, - info = none, - filter = GameFilterMenu.currentOf(GameFilterMenu.all, filterName), - me = ctx.me, - page = page - )(ctx.body) map { filterName -> _ } + val ip = HTTPRequest lastRemoteAddress ctx.req + UserGamesRateLimitPerIP(ip, cost = page, msg = ip) { + GameFilterMenu.paginatorOf( + userGameSearch = userGameSearch, + user = u, + info = none, + filter = GameFilterMenu.currentOf(GameFilterMenu.all, filterName), + me = ctx.me, + page = page + )(ctx.body) + } map { filterName -> _ } } } diff --git a/modules/common/src/main/paginator/Paginator.scala b/modules/common/src/main/paginator/Paginator.scala index 449bd1be2f..d56ccf0892 100644 --- a/modules/common/src/main/paginator/Paginator.scala +++ b/modules/common/src/main/paginator/Paginator.scala @@ -66,6 +66,8 @@ object Paginator { maxPerPage: Int = 10): Fu[Paginator[A]] = validate(adapter, currentPage, maxPerPage) | apply(adapter, 1, maxPerPage) + def empty[A]: Paginator[A] = new Paginator(0, 0, Nil, 0) + def validate[A]( adapter: AdapterLike[A], currentPage: Int = 1,