more msg security

inbox2
Thibault Duplessis 2020-01-26 22:08:17 -06:00
parent d31ea7d929
commit 45c7364b59
4 changed files with 61 additions and 22 deletions

View File

@ -16,7 +16,8 @@ final class Env(
relationApi: lila.relation.RelationApi,
prefApi: lila.pref.PrefApi,
notifyApi: lila.notify.NotifyApi,
cacheApi: lila.memo.CacheApi
cacheApi: lila.memo.CacheApi,
spam: lila.security.Spam
)(implicit ec: scala.concurrent.ExecutionContext, scheduler: akka.actor.Scheduler) {
private val colls = wire[MsgColls]

View File

@ -54,13 +54,14 @@ final class MsgApi(
val msg = Msg.make(text, orig)
val threadId = MsgThread.id(orig, dest)
!colls.thread.exists($id(threadId)) flatMap { isNew =>
security.post(orig, dest, msg, isNew) flatMap {
case MsgSecurity.Ok(mute) =>
security.can.post(dest, msg, isNew) flatMap {
case _: MsgSecurity.Reject => funit
case send: MsgSecurity.Send =>
val msgWrite = colls.msg.insert.one(writeMsg(msg, threadId))
val threadWrite =
if (isNew)
colls.thread.insert.one {
writeThread(MsgThread.make(orig, dest, msg), delBy = mute option dest)
writeThread(MsgThread.make(orig, dest, msg), delBy = send.mute option dest)
}.void
else
colls.thread.update
@ -68,7 +69,7 @@ final class MsgApi(
$id(threadId),
$set("lastMsg" -> msg.asLast) ++ $pull(
// unset deleted by receiver unless the message is muted
"del" $in (orig :: (!mute).option(dest).toList)
"del" $in (orig :: (!send.mute).option(dest).toList)
)
)
.void

View File

@ -3,15 +3,19 @@ package lila.msg
import org.joda.time.DateTime
import scala.concurrent.duration._
import lila.common.Bus
import lila.db.dsl._
import lila.hub.actorApi.report.AutoFlag
import lila.memo.RateLimit
import lila.shutup.Analyser
import lila.user.User
final private class MsgSecurity(
colls: MsgColls,
prefApi: lila.pref.PrefApi,
userRepo: lila.user.UserRepo,
relationApi: lila.relation.RelationApi
relationApi: lila.relation.RelationApi,
spam: lila.security.Spam
)(implicit ec: scala.concurrent.ExecutionContext) {
import BsonHandlers._
@ -31,22 +35,46 @@ final private class MsgSecurity(
key = "msg_reply.user"
)
def post(orig: User.ID, dest: User.ID, msg: Msg, isNew: Boolean): Fu[Verdict] =
may.post(orig, dest) flatMap {
case false => fuccess(Block)
case _ =>
val limiter = if (isNew) CreateLimitPerUser else ReplyLimitPerUser
if (!limiter(orig)(true)) fuccess(Limit)
else
muteTroll(orig, dest) map { troll =>
Ok(mute = troll)
object can {
def post(dest: User.ID, msg: Msg, isNew: Boolean): Fu[Verdict] =
may.post(msg.user, dest) flatMap {
case false => fuccess(Block)
case _ =>
isLimited(msg, isNew) orElse
isSpam(msg) orElse
isTroll(msg.user, dest) orElse
isDirt(msg, isNew) getOrElse
fuccess(Ok)
} flatMap {
case mute: Mute =>
relationApi.fetchFollows(dest, msg.user) dmap { isFriend =>
if (isFriend) Ok else mute
}
case verdict => fuccess(verdict)
} addEffect {
case Dirt =>
Bus.publish(AutoFlag(msg.user, s"msg/${msg.user}/$dest", msg.text), "autoFlag")
case Spam =>
logger.warn(s"PM spam from ${msg.user}: ${msg.text}")
case _ =>
}
private def isLimited(msg: Msg, isNew: Boolean): Fu[Option[Verdict]] = {
val limiter = if (isNew) CreateLimitPerUser else ReplyLimitPerUser
!limiter(msg.user)(true) ?? fuccess(Limit.some)
}
private def muteTroll(orig: User.ID, dest: User.ID): Fu[Boolean] =
userRepo.isTroll(orig) >>&
!userRepo.isTroll(dest) >>&
!relationApi.fetchFollows(dest, orig)
private def isSpam(msg: Msg): Fu[Option[Verdict]] =
spam.detect(msg.text) ?? fuccess(Spam.some)
private def isTroll(orig: User.ID, dest: User.ID): Fu[Option[Verdict]] =
userRepo.isTroll(orig) >>& !userRepo.isTroll(dest) dmap { _ option Troll }
private def isDirt(msg: Msg, isNew: Boolean): Fu[Option[Verdict]] =
(isNew && Analyser(msg.text).dirty) ??
!userRepo.isCreatedSince(msg.user, DateTime.now.minusDays(30)) dmap { _ option Dirt }
}
object may {
@ -81,8 +109,14 @@ final private class MsgSecurity(
private object MsgSecurity {
sealed trait Verdict
sealed trait Reject extends Verdict
sealed abstract class Send(val mute: Boolean) extends Verdict
sealed abstract class Mute extends Send(true)
case class Ok(mute: Boolean) extends Verdict
case object Block extends Verdict
case object Limit extends Verdict
case object Ok extends Send(false)
case object Troll extends Mute
case object Spam extends Mute
case object Dirt extends Mute
case object Block extends Reject
case object Limit extends Reject
}

View File

@ -341,6 +341,9 @@ final class UserRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionCont
def isTroll(id: ID): Fu[Boolean] = coll.exists($id(id) ++ trollSelect(true))
def isCreatedSince(id: ID, since: DateTime): Fu[Boolean] =
coll.exists($id(id) ++ $doc(F.createdAt $lt since))
def setRoles(id: ID, roles: List[String]) = coll.updateField($id(id), F.roles, roles)
def disableTwoFactor(id: ID) = coll.update.one($id(id), $unset(F.totpSecret))