implement forum IP ban

pull/7/merge
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(
categ, topic, posts, Some(err -> forms.captchaCreate))),
data Firewall {
val post = postApi.makePost(categ, topic, data, ctx.me).unsafePerformIO
val post = postApi.makePost(categ, topic, data).unsafePerformIO
Redirect("%s#%d".format(
routes.ForumTopic.show(
categ.slug,

View File

@ -27,7 +27,7 @@ object ForumTopic extends LilaController with forum.Controller {
forms.topic.bindFromRequest.fold(
err BadRequest(html.forum.topic.form(categ, err, forms.captchaCreate)),
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))
}
)

View File

@ -110,6 +110,8 @@ trait LilaController
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)(
implicit writer: Writeable[B],
ctype: ContentTypeOf[B],

View File

@ -31,10 +31,15 @@ object Mod extends LilaController {
def ban(username: String) = Secure(Permission.IpBan) { implicit ctx
me
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
me =>
IOk(modLogApi.recent map { html.mod.log(_) })

View File

@ -12,6 +12,7 @@ case class Post(
topicId: String,
author: Option[String],
userId: Option[String],
ip: Option[String],
text: String,
number: Int,
createdAt: DateTime) {
@ -27,12 +28,14 @@ object Post {
topicId: String,
author: Option[String],
userId: Option[String],
ip: Option[String],
text: String,
number: Int): Post = Post(
id = OrnicarRandom nextString idSize,
topicId = topicId,
author = author,
userId = userId,
ip = ip,
text = text,
number = number,
createdAt = DateTime.now)

View File

@ -2,6 +2,7 @@ package lila
package forum
import user.User
import http.Context
import scalaz.effects._
import com.github.ornicar.paginator._
@ -20,13 +21,13 @@ final class PostApi(env: ForumEnv, maxPerPage: Int) {
def makePost(
categ: Categ,
topic: Topic,
data: DataForm.PostData,
user: Option[User]): IO[Post] = for {
data: DataForm.PostData)(implicit ctx: Context): IO[Post] = for {
number lastNumberOf(topic)
post = Post(
topicId = topic.id,
author = data.author,
userId = user map (_.id),
userId = ctx.me map (_.id),
ip = ctx.isAnon option ctx.req.remoteAddress,
text = data.text,
number = number + 1)
_ env.postRepo saveIO post

View File

@ -2,6 +2,7 @@ package lila
package forum
import user.User
import http.Context
import scalaz.effects._
import com.github.ornicar.paginator._
@ -20,8 +21,7 @@ final class TopicApi(env: ForumEnv, maxPerPage: Int) {
def makeTopic(
categ: Categ,
data: DataForm.TopicData,
user: Option[User]): IO[Topic] = for {
data: DataForm.TopicData)(implicit ctx: Context): IO[Topic] = for {
slug env.topicRepo.nextSlug(categ, data.name)
topic = Topic(
categId = categ.slug,
@ -30,7 +30,8 @@ final class TopicApi(env: ForumEnv, maxPerPage: Int) {
post = Post(
topicId = topic.id,
author = data.post.author,
userId = user map (_.id),
userId = ctx.me map (_.id),
ip = ctx.isAnon option ctx.req.remoteAddress,
text = data.post.text,
number = 1)
_ 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 isAnon = !isAuth
def canSeeChat = me.fold(m !m.isChatBan, false)
def isGranted(permission: Permission): Boolean =

View File

@ -4,7 +4,7 @@ package mod
import user.{ User, UserRepo }
import elo.EloUpdater
import lobby.Messenger
import security.{ Firewall, Store => SecurityStore }
import security.{ Firewall, Store SecurityStore }
import scalaz.effects._
@ -35,15 +35,20 @@ final class ModApi(
io())
} yield ()
def ipban(mod: User, username: String): IO[Unit] = withUser(username) { user
def ban(mod: User, username: String): IO[Unit] = withUser(username) { user
for {
spy securityStore userSpy username
_ io(spy.ips foreach firewall.blockIp)
_ lobbyMessenger mute user.username doUnless user.isChatBan
_ logApi.ipban(mod, user)
_ logApi.ban(mod, user)
} 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 {
userOption userRepo byId username
_ userOption.fold(userIo, io())

View File

@ -10,12 +10,15 @@ case class Modlog(
mod: String,
user: Option[String],
action: String,
details: Option[String] = None,
date: DateTime = DateTime.now) {
def showAction = action match {
case Modlog.engine "mark as engine"
case Modlog.unengine "un-mark as engine"
case Modlog.deletePost "delete forum post"
case Modlog.ban "ban user"
case Modlog.ipban "ban IP"
case a a
}
}
@ -26,6 +29,7 @@ object Modlog {
val unengine = "unengine"
val mute = "mute"
val unmute = "unmute"
val ban = "ban"
val ipban = "ipban"
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))
}
def ipban(mod: User, user: User) = add {
def ban(mod: User, user: User) = add {
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
private def add(modLog: Modlog): IO[Unit] = repo saveIO modLog

View File

@ -7,7 +7,7 @@ import play.api.templates.Html
trait AssetHelper {
val assetVersion = 62
val assetVersion = 63
def cssTag(name: String) = css("stylesheets/" + name)

View File

@ -21,6 +21,11 @@ title = topic.name) {
@authorLink(post, "author".some)
<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>
@if(isGranted(Permission.SuperAdmin)) {
@post.ip.map { ip =>
<a class="ipban" href="@routes.Mod.ipban(ip)">Ban @ip</a>
}
}
@if(isGranted(Permission.ModerateForum)) {
<a class="delete" href="@routes.ForumPost.delete(post.id)">Delete</a>
}

View File

@ -19,6 +19,7 @@
<td>@userIdLink(log.mod.some)</td>
<td>@log.showAction.capitalize</td>
<td>@userIdLink(log.user)</td>
<td>@log.details</td>
</tr>
}
</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/mute controllers.Mod.mute(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
# Wiki

View File

@ -184,6 +184,15 @@ $(function() {
$('input.confirm').click(function() {
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() {
$('span.bookmark a.icon:not(.jsed)').each(function() {

View File

@ -6,7 +6,8 @@ a.forum_feed_link {
float: right;
}
.metas .delete {
.metas .delete,
.metas .ipban {
margin-left: 1em;
}
.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
game stats timeline issues http://en.lichess.org/forum/lichess-feedback/move-times
propagate IP ban to the user
forum post IP ban
admin ip search interface
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
-----------