diff --git a/app/controllers/Round.scala b/app/controllers/Round.scala index 352dbfb7bb..5d0bbe3fdc 100644 --- a/app/controllers/Round.scala +++ b/app/controllers/Round.scala @@ -35,7 +35,7 @@ object Round extends LilaController with TheftPrevention { } def signedJs(gameId: String) = OpenNoCtx { req ⇒ - JsOk(GameRepo token gameId map Env.game.gameJs.sign, CACHE_CONTROL -> "max-age=3600") + JsOk(fuccess(Env.game.gameJs.sign(env.hijack.tokenOf(gameId))), CACHE_CONTROL -> "max-age=3600") } def player(fullId: String) = Open { implicit ctx ⇒ diff --git a/modules/game/src/main/Game.scala b/modules/game/src/main/Game.scala index ee4c97cfab..5c2d425957 100644 --- a/modules/game/src/main/Game.scala +++ b/modules/game/src/main/Game.scala @@ -11,7 +11,6 @@ import lila.user.User case class Game( id: String, - token: String, whitePlayer: Player, blackPlayer: Player, binaryPieces: ByteArray, @@ -340,7 +339,6 @@ object Game { val playerIdSize = 4 val fullIdSize = 12 val tokenSize = 4 - val defaultToken = "-tk-" def abandonedDate = DateTime.now - 7.days @@ -356,7 +354,6 @@ object Game { source: Source, pgnImport: Option[PgnImport]): Game = Game( id = IdGenerator.game, - token = IdGenerator.token, whitePlayer = whitePlayer, blackPlayer = blackPlayer, binaryPieces = if (game.isStandardInit) BinaryFormat.piece.standard @@ -387,7 +384,6 @@ object Game { object BSONFields { val id = "_id" - val token = "tk" val whitePlayer = "p0" val blackPlayer = "p1" val binaryPieces = "ps" @@ -420,7 +416,6 @@ object Game { val nbTurns = r int turns Game( id = r str "_id", - token = r str "tk", whitePlayer = r.get[Color ⇒ Player](whitePlayer)(playerBSONHandler)(White), blackPlayer = r.get[Color ⇒ Player](blackPlayer)(playerBSONHandler)(Black), binaryPieces = r bytes binaryPieces, @@ -448,7 +443,6 @@ object Game { def writes(w: BSON.Writer, o: Game) = BSONDocument( id -> o.id, - token -> o.token, whitePlayer -> ((_: Color) ⇒ o.whitePlayer), blackPlayer -> ((_: Color) ⇒ o.blackPlayer), binaryPieces -> o.binaryPieces, diff --git a/modules/game/src/main/GameBinaryMigration.scala b/modules/game/src/main/GameBinaryMigration.scala index 1b22ef4f8b..0a559fb531 100644 --- a/modules/game/src/main/GameBinaryMigration.scala +++ b/modules/game/src/main/GameBinaryMigration.scala @@ -49,7 +49,7 @@ object GameBinaryMigration { case (k, v) if !drops(k) ⇒ k -> v }) - val gameDrop = Set("c", "cc", "cs", "lm", "lmt", "p", "me", "ph", "uids") + val gameDrop = Set("c", "cc", "cs", "lm", "lmt", "p", "me", "ph", "uids", "tk") val playerDrop = Set("ps", "mts", "uid", "isOfferingDraw", "isOfferingRematch", "isProposingTakeback", "lastDrawOffer") def convertGame(o: Doc): Doc = { diff --git a/modules/game/src/main/GameRepo.scala b/modules/game/src/main/GameRepo.scala index 1474e8f891..c795731ad1 100644 --- a/modules/game/src/main/GameRepo.scala +++ b/modules/game/src/main/GameRepo.scala @@ -72,9 +72,6 @@ trait GameRepo { $query(Query.finished ++ Query.rated ++ Query.user(userId)) sort ($sort asc BSONFields.createdAt) ) - def token(id: ID): Fu[String] = - $primitive.one($select(id), "tk")(_.asOpt[String]) map (_ | Game.defaultToken) - def save(progress: Progress): Funit = GameDiff(progress.origin, progress.game) |> { case (Nil, Nil) ⇒ funit diff --git a/modules/round/src/main/Env.scala b/modules/round/src/main/Env.scala index 0f06924b19..5d3bd6492a 100644 --- a/modules/round/src/main/Env.scala +++ b/modules/round/src/main/Env.scala @@ -154,7 +154,7 @@ final class Env( private lazy val titivate = new Titivate(roundMap, meddler, scheduler) - private lazy val hijack = new Hijack(HijackTimeout, HijackEnabled) + lazy val hijack = new Hijack(HijackTimeout, HijackEnabled) private lazy val takebacker = new Takebacker( messenger = messenger, diff --git a/modules/round/src/main/Hijack.scala b/modules/round/src/main/Hijack.scala index d0ee83ec0e..1b4c80b666 100644 --- a/modules/round/src/main/Hijack.scala +++ b/modules/round/src/main/Hijack.scala @@ -1,20 +1,28 @@ package lila.round -import scala.concurrent.duration.Duration +import scala.concurrent.duration._ -import lila.game.Pov +import lila.game.{ Pov, Game, IdGenerator } import lila.memo.ExpireSetMemo import lila.user.Context private[round] final class Hijack(timeout: Duration, enabled: Boolean) { + // game ID -> game token + private val tokens = lila.memo.Builder.cache( + 2 hour, + (_: String) ⇒ IdGenerator.token + ) + // full game ids that have been hijacked private val hijacks = new ExpireSetMemo(timeout) + def tokenOf(gameId: String) = tokens get gameId + def apply(pov: Pov, token: String): Boolean = enabled && { pov.game.rated && { if (hijacks get pov.fullId) true - else if (token != pov.game.token) { + else if (token != tokenOf(pov.game.id)) { logwarn(s"[websocket] hijacking detected ${pov.fullId}") hijacks put pov.fullId true