protect users coach data: only accessible by friends for now

This commit is contained in:
Thibault Duplessis 2015-07-19 21:56:12 +02:00
parent 1f3bd0a8d3
commit 407be57ec7
9 changed files with 78 additions and 11 deletions

View file

@ -1,7 +1,8 @@
package controllers
import lila.api.Context
import lila.app._
import lila.user.UserRepo
import play.api.mvc.Result
import views._
object Coach extends LilaController {
@ -9,30 +10,42 @@ object Coach extends LilaController {
private def env = Env.coach
def raw(username: String) = Open { implicit ctx =>
OptionFuOk(UserRepo named username) { user =>
Accessible(username) { user =>
env.statApi.fetch(user.id) map { stat =>
html.coach.raw.index(user, stat)
Ok(html.coach.raw.index(user, stat))
}
}
}
def json(username: String) = Open { implicit ctx =>
JsonOptionFuOk(UserRepo named username) { user =>
env.statApi.fetchOrCompute(user.id) flatMap env.jsonView.apply
Accessible(username) { user =>
env.statApi.fetchOrCompute(user.id) flatMap env.jsonView.apply map { json =>
Ok(json) as JSON
}
}
}
def opening(username: String) = Open { implicit ctx =>
OptionFuOk(UserRepo named username) { user =>
Accessible(username) { user =>
env.statApi.fetch(user.id) map { stat =>
html.coach.opening(user, stat)
Ok(html.coach.opening(user, stat))
}
}
}
def refresh(username: String) = Open { implicit ctx =>
OptionFuRedirect(UserRepo named username) { user =>
env.statApi.computeIfOld(user.id) inject routes.Coach.raw(user.username)
Accessible(username) { user =>
env.statApi.computeIfOld(user.id) inject
Redirect(routes.Coach.raw(user.username))
}
}
private def Accessible(username: String)(f: lila.user.User => Fu[Result])(implicit ctx: Context) =
lila.user.UserRepo named username flatMap {
case None => notFound
case Some(u) => env.share.grant(u, ctx.me) flatMap {
case true => f(u)
case false => fuccess(Forbidden(html.coach.forbidden(u)))
}
}
}

View file

@ -95,6 +95,10 @@
<h2>@trans.letOtherPlayersChallengeYou()</h2>
@base.radios(form("challenge"), translatedChallengeChoices)
</li>
<li>
<h2>Share your coach data</h2>
@base.radios(form("coachShare"), lila.pref.Pref.CoachShare.choices)
</li>
</ul>
</fieldset>
<fieldset>

View file

@ -0,0 +1,6 @@
@(u: User)(implicit ctx: Context)
@coach.layout(title = s"${u.username} coach data is protected") {
You cannot see @userLink(u) coach data!
}

View file

@ -8,6 +8,8 @@ import lila.common.PimpedConfig._
final class Env(
config: Config,
getPref: String => Fu[lila.pref.Pref],
areFriends: (String, String) => Fu[Boolean],
db: lila.db.Env) {
private val settings = new {
@ -15,6 +17,8 @@ final class Env(
}
import settings._
lazy val share = new Share(getPref, areFriends)
lazy val jsonView = new JsonView
lazy val statApi = new StatApi(coll = db(CollectionStat))
@ -24,5 +28,7 @@ object Env {
lazy val current: Env = "[boot] coach" describes new Env(
config = lila.common.PlayApp loadConfig "coach",
getPref = lila.pref.Env.current.api.getPrefById,
areFriends = lila.relation.Env.current.api.areFriends,
db = lila.db.Env.current)
}

View file

@ -0,0 +1,18 @@
package lila.coach
import lila.pref.Pref
import lila.user.User
final class Share(
getPref: String => Fu[Pref],
areFriends: (String, String) => Fu[Boolean]) {
def grant(coached: User, to: Option[User]): Fu[Boolean] = getPref(coached.id) flatMap { pref =>
pref.coachShare match {
case _ if to.contains(coached) => fuccess(true)
case Pref.CoachShare.EVERYBODY => fuccess(true)
case Pref.CoachShare.FRIENDS => to ?? { t => areFriends(coached.id, t.id) }
case Pref.CoachShare.NOBODY => fuccess(false)
}
}
}

View file

@ -24,6 +24,7 @@ private[pref] final class DataForm {
"premove" -> number.verifying(Set(0, 1) contains _),
"animation" -> number.verifying(Set(0, 1, 2, 3) contains _),
"submitMove" -> number.verifying(Set(0, 1, 2) contains _),
"coachShare" -> number.verifying(Set(0, 1, 2) contains _),
"captured" -> number.verifying(Set(0, 1) contains _)
)(PrefData.apply)(PrefData.unapply))
@ -44,6 +45,7 @@ private[pref] final class DataForm {
premove: Int,
animation: Int,
submitMove: Int,
coachShare: Int,
captured: Int) {
def apply(pref: Pref) = pref.copy(
@ -63,6 +65,7 @@ private[pref] final class DataForm {
premove = premove == 1,
animation = animation,
submitMove = submitMove,
coachShare = coachShare,
captured = captured == 1)
}
@ -84,6 +87,7 @@ private[pref] final class DataForm {
premove = pref.premove.fold(1, 0),
animation = pref.animation,
submitMove = pref.submitMove,
coachShare = pref.coachShare,
captured = pref.captured.fold(1, 0))
}

View file

@ -36,6 +36,7 @@ case class Pref(
coordColor: Int,
puzzleDifficulty: Int,
submitMove: Int,
coachShare: Int,
tags: Map[String, String] = Map.empty) {
import Pref._
@ -143,6 +144,17 @@ object Pref {
ALWAYS -> "Always")
}
object CoachShare {
val NOBODY = 0
val FRIENDS = 1
val EVERYBODY = 2
val choices = Seq(
NOBODY -> "With nobody",
FRIENDS -> "With friends",
EVERYBODY -> "With everybody")
}
object Blindfold {
val NO = 0
val YES = 1
@ -275,6 +287,7 @@ object Pref {
coordColor = Color.RANDOM,
puzzleDifficulty = Difficulty.NORMAL,
submitMove = SubmitMove.CORRESPONDENCE,
coachShare = CoachShare.FRIENDS,
tags = Map.empty)
import ornicar.scalalib.Zero

View file

@ -54,6 +54,7 @@ final class PrefApi(
coordColor = r.getD("coordColor", Pref.default.coordColor),
puzzleDifficulty = r.getD("puzzleDifficulty", Pref.default.puzzleDifficulty),
submitMove = r.getD("submitMove", Pref.default.submitMove),
coachShare = r.getD("coachShare", Pref.default.coachShare),
tags = r.getD("tags", Pref.default.tags))
def writes(w: BSON.Writer, o: Pref) = BSONDocument(
@ -86,6 +87,7 @@ final class PrefApi(
"coordColor" -> o.coordColor,
"puzzleDifficulty" -> o.puzzleDifficulty,
"submitMove" -> o.submitMove,
"coachShare" -> o.coachShare,
"tags" -> o.tags)
}
@ -95,7 +97,8 @@ final class PrefApi(
BSONDocument("$set" -> BSONDocument(s"tags.$name" -> value)),
upsert = true).void >>- { cache remove user.id }
def getPref(id: String): Fu[Pref] = cache(id) map (_ getOrElse Pref.create(id))
def getPrefById(id: String): Fu[Pref] = cache(id) map (_ getOrElse Pref.create(id))
val getPref = getPrefById _
def getPref(user: User): Fu[Pref] = getPref(user.id)
def getPref(user: Option[User]): Fu[Pref] = user.fold(fuccess(Pref.default))(getPref)

View file

@ -194,7 +194,7 @@ object ApplicationBuild extends Build {
libraryDependencies ++= provided(play.api, RM, PRM)
)
lazy val coach = project("coach", Seq(common, chess, game, user, analyse)).settings(
lazy val coach = project("coach", Seq(common, chess, game, user, analyse, relation, pref)).settings(
libraryDependencies ++= provided(play.api, RM, PRM)
)