implement forum IP ban

This commit is contained in:
Thibault Duplessis 2012-07-22 19:37:38 +02:00
parent 5c5300bfec
commit 5fa937620f
18 changed files with 59 additions and 18 deletions

View file

@ -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,

View file

@ -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))
} }
) )

View file

@ -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],

View file

@ -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(_) })

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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 =

View file

@ -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())

View file

@ -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"
} }

View file

@ -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

View file

@ -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)

View file

@ -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>
} }

View file

@ -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>

View file

@ -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

View file

@ -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() {

View file

@ -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
View file

@ -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
----------- -----------