implement IP ban
This commit is contained in:
parent
87dcd0d97c
commit
62dc62fbb6
|
@ -4,8 +4,10 @@ import lila._
|
|||
import views._
|
||||
|
||||
import play.api.mvc._
|
||||
import play.api.mvc.Results._
|
||||
import play.api.libs.json._
|
||||
import play.api.libs.iteratee._
|
||||
import play.api.libs.concurrent.Akka
|
||||
|
||||
object Main extends LilaController {
|
||||
|
||||
|
@ -15,4 +17,14 @@ object Main extends LilaController {
|
|||
uidOption = get("uid"),
|
||||
username = ctx.me map (_.username))
|
||||
}
|
||||
|
||||
val blocked = Action { implicit req ⇒
|
||||
Async {
|
||||
Akka.future {
|
||||
println("BLOCK %s %s".format(req.remoteAddress, req))
|
||||
Thread sleep 10 * 1000
|
||||
BadRequest(html.base.blocked())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ object User extends LilaController {
|
|||
def lobbyMessenger = env.lobby.messenger
|
||||
def bookmarkApi = env.bookmark.api
|
||||
def securityStore = env.security.store
|
||||
def firewall = env.security.firewall
|
||||
|
||||
def show(username: String) = showFilter(username, "all", 1)
|
||||
|
||||
|
@ -105,6 +106,17 @@ object User extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
def ban(username: String) = Secure(Permission.IpBan) { implicit ctx ⇒
|
||||
_ ⇒
|
||||
IOptionIORedirect(userRepo byId username) { user ⇒
|
||||
for {
|
||||
spy ← securityStore userSpy username
|
||||
_ ← io(spy.ips foreach firewall.block)
|
||||
_ ← user.isChatBan.fold(io(), lobbyMessenger mute username)
|
||||
} yield routes.User show username
|
||||
}
|
||||
}
|
||||
|
||||
val stats = todo
|
||||
|
||||
def export(username: String) = Open { implicit ctx ⇒
|
||||
|
|
|
@ -13,9 +13,10 @@ final class MonoMemo[A](ttl: Int, f: IO[A]) {
|
|||
private var value: A = _
|
||||
private var ts: Double = 0
|
||||
|
||||
private def expired = nowMillis > ts + ttl
|
||||
private def refresh {
|
||||
def refresh {
|
||||
value = f.unsafePerformIO
|
||||
ts = nowMillis
|
||||
}
|
||||
|
||||
private def expired = nowMillis > ts + ttl
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.mongodb.casbah.commons.conversions.scala.RegisterJodaTimeConversionHe
|
|||
package object lila
|
||||
extends OrnicarValidation
|
||||
with OrnicarCommon
|
||||
with OrnicarRegex
|
||||
with scalaz.Identitys
|
||||
with scalaz.NonEmptyLists
|
||||
with scalaz.Strings
|
||||
|
|
|
@ -8,29 +8,41 @@ import play.api.mvc.Results.BadRequest
|
|||
import com.mongodb.casbah.MongoCollection
|
||||
import com.mongodb.casbah.Imports._
|
||||
import scalaz.effects._
|
||||
import java.util.Date
|
||||
|
||||
final class Firewall(
|
||||
collection: MongoCollection,
|
||||
cacheTtl: Int,
|
||||
enabled: Boolean) {
|
||||
|
||||
def requestHandler(req: RequestHeader): Option[Handler] =
|
||||
(enabled && blocks(req.remoteAddress)) option {
|
||||
Action {
|
||||
println("IP block " + req.headers.get("X-Real-IP"))
|
||||
BadRequest("Your IP has been blocked due to abuse.")
|
||||
}
|
||||
}
|
||||
def requestHandler(req: RequestHeader): Option[Handler] =
|
||||
(enabled && blocks(req.remoteAddress)) option controllers.Main.blocked
|
||||
|
||||
def blocks(ip: String) = ips.apply contains ip
|
||||
|
||||
def accepts(ip: String) = !blocks(ip)
|
||||
|
||||
def block(ip: String) {
|
||||
if (validIp(ip)) {
|
||||
if (accepts(ip)) {
|
||||
collection += DBObject("_id" -> ip, "date" -> new Date)
|
||||
ips.refresh
|
||||
}
|
||||
}
|
||||
else println("Invalid IP block: " + ip)
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
|
||||
private val ipRegex = """^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$""".r
|
||||
|
||||
private def validIp(ip: String) =
|
||||
(ipRegex matches ip) && ip != "127.0.0.1" && ip != "0.0.0.0"
|
||||
|
||||
private val ips = new MonoMemo(cacheTtl, fetch)
|
||||
|
||||
private def fetch: IO[Set[String]] = io {
|
||||
collection.find() map { obj ⇒
|
||||
obj.getAs[String]("_id")
|
||||
}
|
||||
}
|
||||
} map (_.flatten.toSet)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ object Permission {
|
|||
case object StaffForum extends Permission("ROLE_STAFF_FORUM")
|
||||
case object ModerateForum extends Permission("ROLE_MODERATE_FORUM")
|
||||
case object UserSpy extends Permission("ROLE_USER_SPY")
|
||||
case object IpBan extends Permission("ROLE_IP_BAN")
|
||||
|
||||
case object Admin extends Permission("ROLE_ADMIN") {
|
||||
override val children = List(
|
||||
|
@ -25,7 +26,8 @@ object Permission {
|
|||
MarkEngine,
|
||||
StaffForum,
|
||||
ModerateForum,
|
||||
UserSpy)
|
||||
UserSpy,
|
||||
IpBan)
|
||||
}
|
||||
case object SuperAdmin extends Permission("ROLE_SUPER_ADMIN") {
|
||||
override val children = List(Admin)
|
||||
|
|
22
app/views/base/blocked.scala.html
Normal file
22
app/views/base/blocked.scala.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
@()
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>lichess - maintenance</title>
|
||||
<style type="text/css">
|
||||
html, body {
|
||||
font:16px monospace;
|
||||
background: #000;
|
||||
color: #ebd488;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>lichess.org is temporarily unreachable</h2>
|
||||
<br />
|
||||
<br />
|
||||
<img src="/assets/images/maintenance.jpg" alt="Lichess maintenance" />
|
||||
</body>
|
||||
</html>
|
|
@ -45,6 +45,11 @@
|
|||
<input class="confirm" type="submit" value="@u.isChatBan.fold("Unmute", "Mute")" />
|
||||
</form>
|
||||
}
|
||||
@if(isGranted(Permission.IpBan)) {
|
||||
<form method="post" action="@routes.User.ban(u.username)">
|
||||
<input class="confirm" type="submit" value="IP ban" />
|
||||
</form>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ session {
|
|||
}
|
||||
firewall {
|
||||
cache_ttl=5 seconds
|
||||
enabled=false
|
||||
enabled=true
|
||||
}
|
||||
|
||||
# trust proxy X-Forwarded-For header
|
||||
|
|
|
@ -70,6 +70,7 @@ GET /@/:username/:filterName controllers.User.showFilter(username: Str
|
|||
GET /@/:username controllers.User.show(username: String)
|
||||
POST /@/:username/engine controllers.User.engine(username: String)
|
||||
POST /@/:username/mute controllers.User.mute(username: String)
|
||||
POST /@/:username/ban controllers.User.ban(username: String)
|
||||
GET /people controllers.User.list(page: Int ?= 1)
|
||||
GET /people/stats controllers.User.stats
|
||||
GET /people/autocomplete controllers.User.autocomplete
|
||||
|
|
|
@ -17,7 +17,7 @@ trait Dependencies {
|
|||
val specs2 = "org.specs2" %% "specs2" % "1.9.2"
|
||||
val casbah = "com.mongodb.casbah" %% "casbah" % "2.1.5-1"
|
||||
val salat = "com.novus" %% "salat-core" % "0.0.8-SNAPSHOT"
|
||||
val scalalib = "com.github.ornicar" %% "scalalib" % "1.30"
|
||||
val scalalib = "com.github.ornicar" %% "scalalib" % "1.33"
|
||||
val config = "com.typesafe" % "config" % "0.4.0"
|
||||
val json = "com.codahale" %% "jerkson" % "0.5.0"
|
||||
val guava = "com.google.guava" % "guava" % "11.0.2"
|
||||
|
|
Loading…
Reference in a new issue