only logged in users can hit elasticearch

pull/7802/head
Thibault Duplessis 2020-12-27 14:25:17 +01:00
parent 7ec9c6d73f
commit ef322d6d27
4 changed files with 121 additions and 83 deletions

View File

@ -1,10 +1,10 @@
package controllers
import scala.concurrent.duration._
import views._
import lila.app._
import lila.common.{ HTTPRequest, IpAddress }
import views._
final class Search(env: Env) extends LilaController(env) {
@ -23,66 +23,74 @@ final class Search(env: Env) extends LilaController(env) {
def index(p: Int) =
OpenBody { implicit ctx =>
NotForBots {
val page = p atLeast 1
Reasonable(page, 100) {
val ip = HTTPRequest ipAddress ctx.req
val cost = scala.math.sqrt(page.toDouble).toInt
implicit def req = ctx.body
env.game.cached.nbTotal flatMap { nbGames =>
def limited =
fuccess {
val form = searchForm
.bindFromRequest()
.withError(
key = "",
message = "Please only send one request at a time per IP address"
)
TooManyRequests(html.search.index(form, none, nbGames))
}
SearchRateLimitPerIP(ip, cost = cost) {
SearchConcurrencyLimitPerIP(ip, limited = limited) {
negotiate(
html = searchForm
env.game.cached.nbTotal flatMap { nbGames =>
if (ctx.isAnon)
negotiate(
html = Unauthorized(html.search.login(nbGames)).fuccess,
api = _ => Unauthorized(jsonError("Login required")).fuccess
)
else
NotForBots {
val page = p atLeast 1
Reasonable(page, 100) {
val ip = HTTPRequest ipAddress ctx.req
val cost = scala.math.sqrt(page.toDouble).toInt
implicit def req = ctx.body
def limited =
fuccess {
val form = searchForm
.bindFromRequest()
.fold(
failure => Ok(html.search.index(failure, none, nbGames)).fuccess,
data =>
data.nonEmptyQuery ?? { query =>
env.gameSearch.paginator(query, page) map some
} map { pager =>
Ok(html.search.index(searchForm fill data, pager, nbGames))
} recover { _ =>
InternalServerError("Sorry, we can't process that query at the moment")
}
),
api = _ =>
searchForm
.withError(
key = "",
message = "Please only send one request at a time per IP address"
)
TooManyRequests(html.search.index(form, none, nbGames))
}
SearchRateLimitPerIP(ip, cost = cost) {
SearchConcurrencyLimitPerIP(ip, limited = limited) {
negotiate(
html = searchForm
.bindFromRequest()
.fold(
_ =>
Ok {
jsonError("Could not process search query")
}.fuccess,
failure => Ok(html.search.index(failure, none, nbGames)).fuccess,
data =>
data.nonEmptyQuery ?? { query =>
env.gameSearch.paginator(query, page) dmap some
} flatMap {
case Some(s) =>
env.api.userGameApi.jsPaginator(s) dmap {
Ok(_)
}
case None =>
BadRequest(jsonError("Could not process search query")).fuccess
env.gameSearch.paginator(query, page) map some
} map { pager =>
Ok(html.search.index(searchForm fill data, pager, nbGames))
} recover { _ =>
InternalServerError(jsonError("Sorry, we can't process that query at the moment"))
InternalServerError("Sorry, we can't process that query at the moment")
}
)
)
}
}(rateLimitedFu)
),
api = _ =>
searchForm
.bindFromRequest()
.fold(
_ =>
Ok {
jsonError("Could not process search query")
}.fuccess,
data =>
data.nonEmptyQuery ?? { query =>
env.gameSearch.paginator(query, page) dmap some
} flatMap {
case Some(s) =>
env.api.userGameApi.jsPaginator(s) dmap {
Ok(_)
}
case None =>
BadRequest(jsonError("Could not process search query")).fuccess
} recover { _ =>
InternalServerError(
jsonError("Sorry, we can't process that query at the moment")
)
}
)
)
}
}(rateLimitedFu)
}
}
}
}
}
}

View File

@ -94,34 +94,40 @@ final class User(
OpenBody { implicit ctx =>
Reasonable(page) {
EnabledUser(username) { u =>
negotiate(
html = for {
nbs <- env.userNbGames(u, ctx, withCrosstable = true)
filters = GameFilterMenu(u, nbs, filter)
pag <- env.gamePaginator(
user = u,
nbs = nbs.some,
filter = filters.current,
me = ctx.me,
page = page
)(ctx.body)
_ <- env.user.lightUserApi preloadMany pag.currentPageResults.flatMap(_.userIds)
_ <- env.tournament.cached.nameCache preloadMany {
pag.currentPageResults.flatMap(_.tournamentId).map(_ -> ctxLang)
}
res <-
if (HTTPRequest isSynchronousHttp ctx.req) for {
info <- env.userInfo(u, nbs, ctx)
_ <- env.team.cached.nameCache preloadMany info.teamIds
social <- env.socialInfo(u, ctx)
searchForm =
(filters.current == GameFilter.Search) option
GameFilterMenu.searchForm(userGameSearch, filters.current)(ctx.body)
} yield html.user.show.page.games(u, info, pag, filters, searchForm, social)
else fuccess(html.user.show.gamesContent(u, nbs, pag, filters, filter))
} yield res,
api = _ => apiGames(u, filter, page)
)
if (filter == "search" && ctx.isAnon)
negotiate(
html = Unauthorized(html.search.login(u.count.game)).fuccess,
api = _ => Unauthorized(jsonError("Login required")).fuccess
)
else
negotiate(
html = for {
nbs <- env.userNbGames(u, ctx, withCrosstable = true)
filters = GameFilterMenu(u, nbs, filter, ctx.isAuth)
pag <- env.gamePaginator(
user = u,
nbs = nbs.some,
filter = filters.current,
me = ctx.me,
page = page
)(ctx.body)
_ <- env.user.lightUserApi preloadMany pag.currentPageResults.flatMap(_.userIds)
_ <- env.tournament.cached.nameCache preloadMany {
pag.currentPageResults.flatMap(_.tournamentId).map(_ -> ctxLang)
}
res <-
if (HTTPRequest isSynchronousHttp ctx.req) for {
info <- env.userInfo(u, nbs, ctx)
_ <- env.team.cached.nameCache preloadMany info.teamIds
social <- env.socialInfo(u, ctx)
searchForm =
(filters.current == GameFilter.Search) option
GameFilterMenu.searchForm(userGameSearch, filters.current)(ctx.body)
} yield html.user.show.page.games(u, info, pag, filters, searchForm, social)
else fuccess(html.user.show.gamesContent(u, nbs, pag, filters, filter))
} yield res,
api = _ => apiGames(u, filter, page)
)
}
}
}

View File

@ -39,7 +39,7 @@ object GameFilterMenu {
val all: NonEmptyList[GameFilter] =
NonEmptyList.of(All, Me, Rated, Win, Loss, Draw, Playing, Bookmark, Imported, Search)
def apply(user: User, nbs: UserInfo.NbGames, currentName: String): GameFilterMenu = {
def apply(user: User, nbs: UserInfo.NbGames, currentName: String, isAuth: Boolean): GameFilterMenu = {
val filters: NonEmptyList[GameFilter] = NonEmptyList(
All,
@ -52,7 +52,7 @@ object GameFilterMenu {
(nbs.playing > 0) option Playing,
(nbs.bookmark > 0) option Bookmark,
(nbs.imported > 0) option Imported,
(user.count.game > 0) option Search
(isAuth && user.count.game > 0) option Search
).flatten
)

View File

@ -0,0 +1,24 @@
package views.html.search
import controllers.routes
import lila.api.Context
import lila.app.templating.Environment._
import lila.app.ui.ScalatagsTemplate._
object login {
def apply(nbGames: Long)(implicit ctx: Context) = {
views.html.base.layout(
title = trans.search.searchInXGames.txt(nbGames.localize, nbGames),
moreCss = cssTag("search")
) {
main(cls := "box box-pad page-small search search-login")(
h1(trans.search.advancedSearch()),
div(cls := "search__login")(
p(a(href := routes.Auth.signup())(trans.youNeedAnAccountToDoThat()))
)
)
}
}
}