implement forum IP ban
parent
5c5300bfec
commit
5fa937620f
|
@ -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,
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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(_) })
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -7,7 +7,7 @@ import play.api.templates.Html
|
|||
|
||||
trait AssetHelper {
|
||||
|
||||
val assetVersion = 62
|
||||
val assetVersion = 63
|
||||
|
||||
def cssTag(name: String) = css("stylesheets/" + name)
|
||||
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
3
todo
|
@ -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
|
||||
-----------
|
||||
|
|
Loading…
Reference in New Issue