implement forum IP ban
This commit is contained in:
parent
5c5300bfec
commit
5fa937620f
|
@ -24,7 +24,7 @@ object ForumPost extends LilaController with forum.Controller {
|
||||||
err ⇒ BadRequest(html.forum.topic.show(
|
err ⇒ BadRequest(html.forum.topic.show(
|
||||||
categ, topic, posts, Some(err -> forms.captchaCreate))),
|
categ, topic, posts, Some(err -> forms.captchaCreate))),
|
||||||
data ⇒ Firewall {
|
data ⇒ Firewall {
|
||||||
val post = postApi.makePost(categ, topic, data, ctx.me).unsafePerformIO
|
val post = postApi.makePost(categ, topic, data).unsafePerformIO
|
||||||
Redirect("%s#%d".format(
|
Redirect("%s#%d".format(
|
||||||
routes.ForumTopic.show(
|
routes.ForumTopic.show(
|
||||||
categ.slug,
|
categ.slug,
|
||||||
|
|
|
@ -27,7 +27,7 @@ object ForumTopic extends LilaController with forum.Controller {
|
||||||
forms.topic.bindFromRequest.fold(
|
forms.topic.bindFromRequest.fold(
|
||||||
err ⇒ BadRequest(html.forum.topic.form(categ, err, forms.captchaCreate)),
|
err ⇒ BadRequest(html.forum.topic.form(categ, err, forms.captchaCreate)),
|
||||||
data ⇒ Firewall {
|
data ⇒ Firewall {
|
||||||
val topic = topicApi.makeTopic(categ, data, ctx.me).unsafePerformIO
|
val topic = topicApi.makeTopic(categ, data).unsafePerformIO
|
||||||
Redirect(routes.ForumTopic.show(categ.slug, topic.slug, 1))
|
Redirect(routes.ForumTopic.show(categ.slug, topic.slug, 1))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -110,6 +110,8 @@ trait LilaController
|
||||||
|
|
||||||
def IORedirect(op: IO[Call]) = Redirect(op.unsafePerformIO)
|
def IORedirect(op: IO[Call]) = Redirect(op.unsafePerformIO)
|
||||||
|
|
||||||
|
def IORedirectUrl(op: IO[String]) = Redirect(op.unsafePerformIO)
|
||||||
|
|
||||||
def OptionOk[A, B](oa: Option[A])(op: A ⇒ B)(
|
def OptionOk[A, B](oa: Option[A])(op: A ⇒ B)(
|
||||||
implicit writer: Writeable[B],
|
implicit writer: Writeable[B],
|
||||||
ctype: ContentTypeOf[B],
|
ctype: ContentTypeOf[B],
|
||||||
|
|
|
@ -31,10 +31,15 @@ object Mod extends LilaController {
|
||||||
def ban(username: String) = Secure(Permission.IpBan) { implicit ctx ⇒
|
def ban(username: String) = Secure(Permission.IpBan) { implicit ctx ⇒
|
||||||
me ⇒
|
me ⇒
|
||||||
IORedirect {
|
IORedirect {
|
||||||
modApi.ipban(me, username) map { _ ⇒ routes.User show username }
|
modApi.ban(me, username) map { _ ⇒ routes.User show username }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def ipban(ip: String) = Secure(Permission.IpBan) { implicit ctx ⇒
|
||||||
|
me ⇒
|
||||||
|
IOk(modApi.ipban(me, ip))
|
||||||
|
}
|
||||||
|
|
||||||
val log = Auth { implicit ctx ⇒
|
val log = Auth { implicit ctx ⇒
|
||||||
me =>
|
me =>
|
||||||
IOk(modLogApi.recent map { html.mod.log(_) })
|
IOk(modLogApi.recent map { html.mod.log(_) })
|
||||||
|
|
|
@ -12,6 +12,7 @@ case class Post(
|
||||||
topicId: String,
|
topicId: String,
|
||||||
author: Option[String],
|
author: Option[String],
|
||||||
userId: Option[String],
|
userId: Option[String],
|
||||||
|
ip: Option[String],
|
||||||
text: String,
|
text: String,
|
||||||
number: Int,
|
number: Int,
|
||||||
createdAt: DateTime) {
|
createdAt: DateTime) {
|
||||||
|
@ -27,12 +28,14 @@ object Post {
|
||||||
topicId: String,
|
topicId: String,
|
||||||
author: Option[String],
|
author: Option[String],
|
||||||
userId: Option[String],
|
userId: Option[String],
|
||||||
|
ip: Option[String],
|
||||||
text: String,
|
text: String,
|
||||||
number: Int): Post = Post(
|
number: Int): Post = Post(
|
||||||
id = OrnicarRandom nextString idSize,
|
id = OrnicarRandom nextString idSize,
|
||||||
topicId = topicId,
|
topicId = topicId,
|
||||||
author = author,
|
author = author,
|
||||||
userId = userId,
|
userId = userId,
|
||||||
|
ip = ip,
|
||||||
text = text,
|
text = text,
|
||||||
number = number,
|
number = number,
|
||||||
createdAt = DateTime.now)
|
createdAt = DateTime.now)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package lila
|
||||||
package forum
|
package forum
|
||||||
|
|
||||||
import user.User
|
import user.User
|
||||||
|
import http.Context
|
||||||
|
|
||||||
import scalaz.effects._
|
import scalaz.effects._
|
||||||
import com.github.ornicar.paginator._
|
import com.github.ornicar.paginator._
|
||||||
|
@ -20,13 +21,13 @@ final class PostApi(env: ForumEnv, maxPerPage: Int) {
|
||||||
def makePost(
|
def makePost(
|
||||||
categ: Categ,
|
categ: Categ,
|
||||||
topic: Topic,
|
topic: Topic,
|
||||||
data: DataForm.PostData,
|
data: DataForm.PostData)(implicit ctx: Context): IO[Post] = for {
|
||||||
user: Option[User]): IO[Post] = for {
|
|
||||||
number ← lastNumberOf(topic)
|
number ← lastNumberOf(topic)
|
||||||
post = Post(
|
post = Post(
|
||||||
topicId = topic.id,
|
topicId = topic.id,
|
||||||
author = data.author,
|
author = data.author,
|
||||||
userId = user map (_.id),
|
userId = ctx.me map (_.id),
|
||||||
|
ip = ctx.isAnon option ctx.req.remoteAddress,
|
||||||
text = data.text,
|
text = data.text,
|
||||||
number = number + 1)
|
number = number + 1)
|
||||||
_ ← env.postRepo saveIO post
|
_ ← env.postRepo saveIO post
|
||||||
|
|
|
@ -2,6 +2,7 @@ package lila
|
||||||
package forum
|
package forum
|
||||||
|
|
||||||
import user.User
|
import user.User
|
||||||
|
import http.Context
|
||||||
|
|
||||||
import scalaz.effects._
|
import scalaz.effects._
|
||||||
import com.github.ornicar.paginator._
|
import com.github.ornicar.paginator._
|
||||||
|
@ -20,8 +21,7 @@ final class TopicApi(env: ForumEnv, maxPerPage: Int) {
|
||||||
|
|
||||||
def makeTopic(
|
def makeTopic(
|
||||||
categ: Categ,
|
categ: Categ,
|
||||||
data: DataForm.TopicData,
|
data: DataForm.TopicData)(implicit ctx: Context): IO[Topic] = for {
|
||||||
user: Option[User]): IO[Topic] = for {
|
|
||||||
slug ← env.topicRepo.nextSlug(categ, data.name)
|
slug ← env.topicRepo.nextSlug(categ, data.name)
|
||||||
topic = Topic(
|
topic = Topic(
|
||||||
categId = categ.slug,
|
categId = categ.slug,
|
||||||
|
@ -30,7 +30,8 @@ final class TopicApi(env: ForumEnv, maxPerPage: Int) {
|
||||||
post = Post(
|
post = Post(
|
||||||
topicId = topic.id,
|
topicId = topic.id,
|
||||||
author = data.post.author,
|
author = data.post.author,
|
||||||
userId = user map (_.id),
|
userId = ctx.me map (_.id),
|
||||||
|
ip = ctx.isAnon option ctx.req.remoteAddress,
|
||||||
text = data.post.text,
|
text = data.post.text,
|
||||||
number = 1)
|
number = 1)
|
||||||
_ ← env.postRepo saveIO post
|
_ ← env.postRepo saveIO post
|
||||||
|
|
|
@ -10,6 +10,8 @@ sealed abstract class Context(val req: RequestHeader, val me: Option[User]) {
|
||||||
|
|
||||||
def isAuth = me.isDefined
|
def isAuth = me.isDefined
|
||||||
|
|
||||||
|
def isAnon = !isAuth
|
||||||
|
|
||||||
def canSeeChat = me.fold(m ⇒ !m.isChatBan, false)
|
def canSeeChat = me.fold(m ⇒ !m.isChatBan, false)
|
||||||
|
|
||||||
def isGranted(permission: Permission): Boolean =
|
def isGranted(permission: Permission): Boolean =
|
||||||
|
|
|
@ -4,7 +4,7 @@ package mod
|
||||||
import user.{ User, UserRepo }
|
import user.{ User, UserRepo }
|
||||||
import elo.EloUpdater
|
import elo.EloUpdater
|
||||||
import lobby.Messenger
|
import lobby.Messenger
|
||||||
import security.{ Firewall, Store => SecurityStore }
|
import security.{ Firewall, Store ⇒ SecurityStore }
|
||||||
|
|
||||||
import scalaz.effects._
|
import scalaz.effects._
|
||||||
|
|
||||||
|
@ -35,15 +35,20 @@ final class ModApi(
|
||||||
io())
|
io())
|
||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
def ipban(mod: User, username: String): IO[Unit] = withUser(username) { user ⇒
|
def ban(mod: User, username: String): IO[Unit] = withUser(username) { user ⇒
|
||||||
for {
|
for {
|
||||||
spy ← securityStore userSpy username
|
spy ← securityStore userSpy username
|
||||||
_ ← io(spy.ips foreach firewall.blockIp)
|
_ ← io(spy.ips foreach firewall.blockIp)
|
||||||
_ ← lobbyMessenger mute user.username doUnless user.isChatBan
|
_ ← lobbyMessenger mute user.username doUnless user.isChatBan
|
||||||
_ ← logApi.ipban(mod, user)
|
_ ← logApi.ban(mod, user)
|
||||||
} yield ()
|
} yield ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def ipban(mod: User, ip: String): IO[Unit] = for {
|
||||||
|
_ ← io(firewall blockIp ip)
|
||||||
|
_ ← logApi.ipban(mod, ip)
|
||||||
|
} yield ()
|
||||||
|
|
||||||
private def withUser(username: String)(userIo: User ⇒ IO[Unit]) = for {
|
private def withUser(username: String)(userIo: User ⇒ IO[Unit]) = for {
|
||||||
userOption ← userRepo byId username
|
userOption ← userRepo byId username
|
||||||
_ ← userOption.fold(userIo, io())
|
_ ← userOption.fold(userIo, io())
|
||||||
|
|
|
@ -10,12 +10,15 @@ case class Modlog(
|
||||||
mod: String,
|
mod: String,
|
||||||
user: Option[String],
|
user: Option[String],
|
||||||
action: String,
|
action: String,
|
||||||
|
details: Option[String] = None,
|
||||||
date: DateTime = DateTime.now) {
|
date: DateTime = DateTime.now) {
|
||||||
|
|
||||||
def showAction = action match {
|
def showAction = action match {
|
||||||
case Modlog.engine ⇒ "mark as engine"
|
case Modlog.engine ⇒ "mark as engine"
|
||||||
case Modlog.unengine ⇒ "un-mark as engine"
|
case Modlog.unengine ⇒ "un-mark as engine"
|
||||||
case Modlog.deletePost ⇒ "delete forum post"
|
case Modlog.deletePost ⇒ "delete forum post"
|
||||||
|
case Modlog.ban ⇒ "ban user"
|
||||||
|
case Modlog.ipban ⇒ "ban IP"
|
||||||
case a ⇒ a
|
case a ⇒ a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +29,7 @@ object Modlog {
|
||||||
val unengine = "unengine"
|
val unengine = "unengine"
|
||||||
val mute = "mute"
|
val mute = "mute"
|
||||||
val unmute = "unmute"
|
val unmute = "unmute"
|
||||||
|
val ban = "ban"
|
||||||
val ipban = "ipban"
|
val ipban = "ipban"
|
||||||
val deletePost = "deletePost"
|
val deletePost = "deletePost"
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,14 @@ final class ModlogApi(repo: ModlogRepo) {
|
||||||
Modlog(mod.id, user.id.some, v.fold(Modlog.mute, Modlog.unmute))
|
Modlog(mod.id, user.id.some, v.fold(Modlog.mute, Modlog.unmute))
|
||||||
}
|
}
|
||||||
|
|
||||||
def ipban(mod: User, user: User) = add {
|
def ban(mod: User, user: User) = add {
|
||||||
Modlog(mod.id, user.id.some, Modlog.ipban)
|
Modlog(mod.id, user.id.some, Modlog.ipban)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def ipban(mod: User, ip: String) = add {
|
||||||
|
Modlog(mod.id, none, Modlog.ipban, ip.some)
|
||||||
|
}
|
||||||
|
|
||||||
def recent = repo recent 200
|
def recent = repo recent 200
|
||||||
|
|
||||||
private def add(modLog: Modlog): IO[Unit] = repo saveIO modLog
|
private def add(modLog: Modlog): IO[Unit] = repo saveIO modLog
|
||||||
|
|
|
@ -7,7 +7,7 @@ import play.api.templates.Html
|
||||||
|
|
||||||
trait AssetHelper {
|
trait AssetHelper {
|
||||||
|
|
||||||
val assetVersion = 62
|
val assetVersion = 63
|
||||||
|
|
||||||
def cssTag(name: String) = css("stylesheets/" + name)
|
def cssTag(name: String) = css("stylesheets/" + name)
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,11 @@ title = topic.name) {
|
||||||
@authorLink(post, "author".some)
|
@authorLink(post, "author".some)
|
||||||
<span class="createdAt">@showDate(post.createdAt)</span>
|
<span class="createdAt">@showDate(post.createdAt)</span>
|
||||||
<a class="anchor" href="@routes.ForumTopic.show(categ.slug, topic.slug, posts.currentPage)#@post.number">#@post.number</a>
|
<a class="anchor" href="@routes.ForumTopic.show(categ.slug, topic.slug, posts.currentPage)#@post.number">#@post.number</a>
|
||||||
|
@if(isGranted(Permission.SuperAdmin)) {
|
||||||
|
@post.ip.map { ip =>
|
||||||
|
<a class="ipban" href="@routes.Mod.ipban(ip)">Ban @ip</a>
|
||||||
|
}
|
||||||
|
}
|
||||||
@if(isGranted(Permission.ModerateForum)) {
|
@if(isGranted(Permission.ModerateForum)) {
|
||||||
<a class="delete" href="@routes.ForumPost.delete(post.id)">Delete</a>
|
<a class="delete" href="@routes.ForumPost.delete(post.id)">Delete</a>
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
<td>@userIdLink(log.mod.some)</td>
|
<td>@userIdLink(log.mod.some)</td>
|
||||||
<td>@log.showAction.capitalize</td>
|
<td>@log.showAction.capitalize</td>
|
||||||
<td>@userIdLink(log.user)</td>
|
<td>@userIdLink(log.user)</td>
|
||||||
|
<td>@log.details</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody></table>
|
</tbody></table>
|
||||||
|
|
|
@ -88,6 +88,7 @@ POST /account/closeConfirm controllers.User.closeConfirm
|
||||||
POST /mod/:username/engine controllers.Mod.engine(username: String)
|
POST /mod/:username/engine controllers.Mod.engine(username: String)
|
||||||
POST /mod/:username/mute controllers.Mod.mute(username: String)
|
POST /mod/:username/mute controllers.Mod.mute(username: String)
|
||||||
POST /mod/:username/ban controllers.Mod.ban(username: String)
|
POST /mod/:username/ban controllers.Mod.ban(username: String)
|
||||||
|
POST /mod/:ip/ipban controllers.Mod.ipban(ip: String)
|
||||||
GET /mod/log controllers.Mod.log
|
GET /mod/log controllers.Mod.log
|
||||||
|
|
||||||
# Wiki
|
# Wiki
|
||||||
|
|
|
@ -184,6 +184,15 @@ $(function() {
|
||||||
$('input.confirm').click(function() {
|
$('input.confirm').click(function() {
|
||||||
return confirm('Confirm this action?');
|
return confirm('Confirm this action?');
|
||||||
});
|
});
|
||||||
|
$('a.ipban').one("click", function() {
|
||||||
|
var $a = $(this);
|
||||||
|
if (confirm($a.text() + "?")) {
|
||||||
|
$.post($a.attr('href'), function() {
|
||||||
|
$a.text('Done').attr('href', '#');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
function bookmarks() {
|
function bookmarks() {
|
||||||
$('span.bookmark a.icon:not(.jsed)').each(function() {
|
$('span.bookmark a.icon:not(.jsed)').each(function() {
|
||||||
|
|
|
@ -6,7 +6,8 @@ a.forum_feed_link {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metas .delete {
|
.metas .delete,
|
||||||
|
.metas .ipban {
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
.forum_topics_list .delete {
|
.forum_topics_list .delete {
|
||||||
|
|
3
todo
3
todo
|
@ -32,11 +32,8 @@ finish games per day chart
|
||||||
untranslated = https://github.com/ornicar/lila/issues/4
|
untranslated = https://github.com/ornicar/lila/issues/4
|
||||||
game stats timeline issues http://en.lichess.org/forum/lichess-feedback/move-times
|
game stats timeline issues http://en.lichess.org/forum/lichess-feedback/move-times
|
||||||
propagate IP ban to the user
|
propagate IP ban to the user
|
||||||
forum post IP ban
|
|
||||||
admin ip search interface
|
admin ip search interface
|
||||||
analyse: show main line for every move http://en.lichess.org/forum/lichess-feedback/about-the-analysis-feature#5
|
analyse: show main line for every move http://en.lichess.org/forum/lichess-feedback/about-the-analysis-feature#5
|
||||||
show victory in analysis page + other suggestions http://en.lichess.org/forum/lichess-feedback/a-couple-of-feature-requests#2
|
|
||||||
only one stockfish instance for moves and analysis. Use a todo with two queues.
|
|
||||||
|
|
||||||
next deploy
|
next deploy
|
||||||
-----------
|
-----------
|
||||||
|
|
Loading…
Reference in a new issue