lila/app/controllers/Account.scala
2019-12-08 02:02:12 -06:00

296 lines
9.4 KiB
Scala

package controllers
import play.api.libs.json._
import play.api.mvc._
import lila.api.Context
import lila.app._
import lila.user.{ User => UserModel, TotpSecret }
import views.html
final class Account(
env: Env,
auth: Auth
) extends LilaController(env) {
def profile = Auth { implicit ctx => me =>
Ok(html.account.profile(me, env.user.forms profileOf me)).fuccess
}
def username = Auth { implicit ctx => me =>
Ok(html.account.username(me, env.user.forms usernameOf me)).fuccess
}
def profileApply = AuthBody { implicit ctx => me =>
implicit val req: Request[_] = ctx.body
FormFuResult(env.user.forms.profile) { err =>
fuccess(html.account.profile(me, err))
} { profile =>
env.user.repo.setProfile(me.id, profile) inject Redirect(routes.User show me.username)
}
}
def usernameApply = AuthBody { implicit ctx => me =>
implicit val req: Request[_] = ctx.body
FormFuResult(env.user.forms.username(me)) { err =>
fuccess(html.account.username(me, err))
} { username =>
env.user.repo.setUsernameCased(me.id, username) inject Redirect(routes.User show me.username) recoverWith {
case e => fuccess(html.account.username(me, env.user.forms.username(me).withGlobalError(e.getMessage)))
}
}
}
def info = Auth { implicit ctx => me =>
negotiate(
html = notFound,
api = _ => {
env.relation.api.countFollowers(me.id) zip
env.relation.api.countFollowing(me.id) zip
env.pref.api.getPref(me) zip
env.round.proxyRepo.urgentGames(me) zip
env.challenge.api.countInFor.get(me.id) zip
env.playban.api.currentBan(me.id) map {
case nbFollowers ~ nbFollowing ~ prefs ~ povs ~ nbChallenges ~ playban =>
Ok {
import lila.pref.JsonView._
env.user.jsonView(me) ++ Json.obj(
"prefs" -> prefs,
"nowPlaying" -> JsArray(povs take 50 map env.api.lobbyApi.nowPlaying),
"nbFollowing" -> nbFollowing,
"nbFollowers" -> nbFollowers,
"nbChallenges" -> nbChallenges
).add("kid" -> me.kid)
.add("troll" -> me.troll)
.add("playban" -> playban)
}
}
}
)
}
def nowPlaying = Auth { implicit ctx => me =>
negotiate(
html = notFound,
api = _ => doNowPlaying(me, ctx.req)
)
}
def apiMe = Scoped() { _ => me =>
env.api.userApi.extended(me, me.some) dmap { JsonOk(_) }
}
def apiNowPlaying = Scoped() { req => me =>
doNowPlaying(me, req)
}
private def doNowPlaying(me: lila.user.User, req: RequestHeader) =
env.round.proxyRepo.urgentGames(me) map { povs =>
val nb = (getInt("nb", req) | 9) atMost 50
Ok(Json.obj("nowPlaying" -> JsArray(povs take nb map env.api.lobbyApi.nowPlaying)))
}
def dasher = Auth { implicit ctx => me =>
negotiate(
html = notFound,
api = _ => env.pref.api.getPref(me) map { prefs =>
Ok {
import lila.pref.JsonView._
lila.common.LightUser.lightUserWrites.writes(me.light) ++ Json.obj(
"coach" -> isGranted(_.Coach),
"prefs" -> prefs
)
}
}
)
}
def passwd = Auth { implicit ctx => me =>
env.user.forms passwd me map { form =>
Ok(html.account.passwd(form))
}
}
def passwdApply = AuthBody { implicit ctx => me =>
auth.HasherRateLimit(me.username, ctx.req) { _ =>
implicit val req = ctx.body
env.user.forms passwd me flatMap { form =>
FormFuResult(form) { err =>
fuccess(html.account.passwd(err))
} { data =>
env.user.authenticator.setPassword(me.id, UserModel.ClearPassword(data.newPasswd1)) inject
Redirect(s"${routes.Account.passwd}?ok=1")
}
}
}
}
private def emailForm(user: UserModel) = env.user.repo email user.id flatMap {
env.security.forms.changeEmail(user, _)
}
def email = Auth { implicit ctx => me =>
if (getBool("check")) Ok(renderCheckYourEmail).fuccess
else emailForm(me) map { form =>
Ok(html.account.email(me, form))
}
}
def apiEmail = Scoped(_.Email.Read) { _ => me =>
env.user.repo email me.id map {
_ ?? { email =>
JsonOk(Json.obj("email" -> email.value))
}
}
}
def renderCheckYourEmail(implicit ctx: Context) =
html.auth.checkYourEmail(lila.security.EmailConfirm.cookie get ctx.req)
def emailApply = AuthBody { implicit ctx => me =>
auth.HasherRateLimit(me.username, ctx.req) { _ =>
implicit val req = ctx.body
env.security.forms.preloadEmailDns >> emailForm(me).flatMap { form =>
FormFuResult(form) { err =>
fuccess(html.account.email(me, err))
} { data =>
val email = env.security.emailAddressValidator.validate(data.realEmail) err s"Invalid email ${data.email}"
val newUserEmail = lila.security.EmailConfirm.UserEmail(me.username, email.acceptable)
auth.EmailConfirmRateLimit(newUserEmail, ctx.req) {
env.security.emailChange.send(me, newUserEmail.email) inject Redirect {
s"${routes.Account.email}?check=1"
}
}
}
}
}
}
def emailConfirm(token: String) = Open { implicit ctx =>
env.security.emailChange.confirm(token) flatMap {
_ ?? { user =>
auth.authenticateUser(user, result = Some { _ =>
Redirect(s"${routes.Account.email}?ok=1")
})
}
}
}
def emailConfirmHelp = OpenBody { implicit ctx =>
import lila.security.EmailConfirm.Help._
ctx.me match {
case Some(me) =>
Redirect(routes.User.show(me.username)).fuccess
case None if get("username").isEmpty =>
Ok(html.account.emailConfirmHelp(helpForm, none)).fuccess
case None =>
implicit val req = ctx.body
helpForm.bindFromRequest.fold(
err => BadRequest(html.account.emailConfirmHelp(err, none)).fuccess,
username => getStatus(env.user.repo, username) map { status =>
Ok(html.account.emailConfirmHelp(helpForm fill username, status.some))
}
)
}
}
def twoFactor = Auth { implicit ctx => me =>
if (me.totpSecret.isDefined)
env.security.forms.disableTwoFactor(me) map { form =>
html.account.twoFactor.disable(me, form)
}
else
env.security.forms.setupTwoFactor(me) map { form =>
html.account.twoFactor.setup(me, form)
}
}
def setupTwoFactor = AuthBody { implicit ctx => me =>
auth.HasherRateLimit(me.username, ctx.req) { _ =>
implicit val req = ctx.body
val currentSessionId = ~env.security.api.reqSessionId(ctx.req)
env.security.forms.setupTwoFactor(me) flatMap { form =>
FormFuResult(form) { err =>
fuccess(html.account.twoFactor.setup(me, err))
} { data =>
env.user.repo.setupTwoFactor(me.id, TotpSecret(data.secret)) >>
env.security.store.closeUserExceptSessionId(me.id, currentSessionId) >>
env.push.webSubscriptionApi.unsubscribeByUserExceptSession(me, currentSessionId) inject
Redirect(routes.Account.twoFactor)
}
}
}
}
def disableTwoFactor = AuthBody { implicit ctx => me =>
auth.HasherRateLimit(me.username, ctx.req) { _ =>
implicit val req = ctx.body
env.security.forms.disableTwoFactor(me) flatMap { form =>
FormFuResult(form) { err =>
fuccess(html.account.twoFactor.disable(me, err))
} { _ =>
env.user.repo.disableTwoFactor(me.id) inject Redirect(routes.Account.twoFactor)
}
}
}
}
def close = Auth { implicit ctx => me =>
env.security.forms closeAccount me map { form =>
Ok(html.account.close(me, form))
}
}
def closeConfirm = AuthBody { implicit ctx => me =>
implicit val req = ctx.body
env.security.forms closeAccount me flatMap { form =>
FormFuResult(form) { err =>
fuccess(html.account.close(me, err))
} { _ =>
env.closeAccount(me.id, self = true) inject {
Redirect(routes.User show me.username) withCookies env.lilaCookie.newSession
}
}
}
}
def kid = Auth { implicit ctx => me =>
Ok(html.account.kid(me)).fuccess
}
def apiKid = Scoped(_.Preference.Read) { _ => me =>
JsonOk(Json.obj("kid" -> me.kid)).fuccess
}
// App BC
def kidToggle = Auth { _ => me =>
env.user.repo.setKid(me, !me.kid) inject Ok
}
def kidPost = Auth { implicit ctx => me =>
env.user.repo.setKid(me, getBool("v")) inject Redirect(routes.Account.kid)
}
def apiKidPost = Scoped(_.Preference.Write) { req => me =>
env.user.repo.setKid(me, getBool("v", req)) inject jsonOkResult
}
private def currentSessionId(implicit ctx: Context) =
~env.security.api.reqSessionId(ctx.req)
def security = Auth { implicit ctx => me =>
env.security.api.dedup(me.id, ctx.req) >>
env.security.api.locatedOpenSessions(me.id, 50) map { sessions =>
Ok(html.account.security(me, sessions, currentSessionId))
}
}
def signout(sessionId: String) = Auth { implicit _ctx => me =>
if (sessionId == "all")
env.security.store.closeUserExceptSessionId(me.id, currentSessionId) >>
env.push.webSubscriptionApi.unsubscribeByUserExceptSession(me, currentSessionId) inject
Redirect(routes.Account.security)
else
env.security.store.closeUserAndSessionId(me.id, sessionId) >>
env.push.webSubscriptionApi.unsubscribeBySession(sessionId)
}
}