lila/app/controllers/Auth.scala
Thibault Duplessis 6a46588eae login autoref
2016-08-10 00:45:39 +02:00

226 lines
7.9 KiB
Scala

package controllers
import play.api.data._, Forms._
import play.api.i18n.Messages.Implicits._
import play.api.libs.json._
import play.api.mvc._, Results._
import play.api.Play.current
import lila.api.Context
import lila.app._
import lila.common.{ LilaCookie, HTTPRequest }
import lila.user.{ UserRepo, User => UserModel }
import views._
object Auth extends LilaController {
private def env = Env.security
private def api = env.api
private def forms = env.forms
private def mobileUserOk(u: UserModel): Fu[Result] =
lila.game.GameRepo urgentGames u map { povs =>
Ok {
Env.user.jsonView(u) ++ Json.obj(
"nowPlaying" -> JsArray(povs take 20 map Env.api.lobbyApi.nowPlaying))
}
}
private def authenticateUser(u: UserModel)(implicit ctx: Context): Fu[Result] = {
implicit val req = ctx.req
u.ipBan.fold(
fuccess(Redirect(routes.Lobby.home)),
api.saveAuthentication(u.id, ctx.mobileApiVersion) flatMap { sessionId =>
negotiate(
html = Redirect {
get("referrer").filter(_.nonEmpty) orElse req.session.get(api.AccessUri) getOrElse routes.Lobby.home.url
}.fuccess,
api = _ => mobileUserOk(u)
) map {
_ withCookies LilaCookie.withSession { session =>
session + ("sessionId" -> sessionId) - api.AccessUri
}
}
} recoverWith authRecovery
)
}
private def authRecovery(implicit ctx: Context): PartialFunction[Throwable, Fu[Result]] = {
case lila.security.Api.MustConfirmEmail(userId) => UserRepo byId userId map {
case Some(user) => BadRequest(html.auth.checkYourEmail(user))
case None => BadRequest
}
}
def login = Open { implicit ctx =>
val referrer = get("referrer") orElse {
getBool("autoref") ?? HTTPRequest.referer(ctx.req)
}
Ok(html.auth.login(api.loginForm, referrer)).fuccess
}
def authenticate = OpenBody { implicit ctx =>
Firewall {
implicit val req = ctx.body
val referrer = get("referrer")
api.usernameForm.bindFromRequest.fold(
err => negotiate(
html = Unauthorized(html.auth.login(api.loginForm, referrer)).fuccess,
api = _ => Unauthorized(errorsAsJson(err)).fuccess
),
username => api.loadLoginForm(username) flatMap { loginForm =>
loginForm.bindFromRequest.fold(
err => negotiate(
html = Unauthorized(html.auth.login(err, referrer)).fuccess,
api = _ => Unauthorized(errorsAsJson(err)).fuccess
), {
case None => InternalServerError("Authentication error").fuccess
case Some(u) => authenticateUser(u)
})
})
}
}
def logout = Open { implicit ctx =>
implicit val req = ctx.req
req.session get "sessionId" foreach lila.security.Store.delete
negotiate(
html = fuccess(Redirect(routes.Main.mobile)),
api = apiVersion => Ok(Json.obj("ok" -> true)).fuccess
) map (_ withCookies LilaCookie.newSession)
}
def signup = Open { implicit ctx =>
NoTor {
Ok(html.auth.signup(forms.signup.website, env.RecaptchaPublicKey)).fuccess
}
}
def signupPost = OpenBody { implicit ctx =>
implicit val req = ctx.body
NoTor {
Firewall {
negotiate(
html = forms.signup.website.bindFromRequest.fold(
err => BadRequest(html.auth.signup(err, env.RecaptchaPublicKey)).fuccess,
data => env.recaptcha.verify(~data.recaptchaResponse, req).flatMap {
case false => BadRequest(html.auth.signup(forms.signup.website fill data, env.RecaptchaPublicKey)).fuccess
case true =>
lila.mon.user.register.website()
val email = env.emailAddress.validate(data.email) err s"Invalid email ${data.email}"
UserRepo.create(data.username, data.password, email.some, ctx.blindMode, none)
.flatten(s"No user could be created for ${data.username}")
.map(_ -> email).flatMap {
case (user, email) => env.emailConfirm.send(user, email) >> {
if (env.emailConfirm.effective) Redirect(routes.Auth.checkYourEmail(user.username)).fuccess
else redirectNewUser(user)
}
}
}),
api = apiVersion => forms.signup.mobile.bindFromRequest.fold(
err => fuccess(BadRequest(jsonError(errorsAsJson(err)))),
data => {
lila.mon.user.register.mobile()
val email = data.email flatMap env.emailAddress.validate
UserRepo.create(data.username, data.password, email, false, apiVersion.some)
.flatten(s"No user could be created for ${data.username}") flatMap authenticateUser
}
)
)
}
}
}
def checkYourEmail(name: String) = Open { implicit ctx =>
OptionOk(UserRepo named name) { user =>
html.auth.checkYourEmail(user)
}
}
def signupConfirmEmail(token: String) = Open { implicit ctx =>
Env.security.emailConfirm.confirm(token) flatMap {
_.fold(notFound)(redirectNewUser)
}
}
private def redirectNewUser(user: UserModel)(implicit ctx: Context) = {
implicit val req = ctx.req
api.saveAuthentication(user.id, ctx.mobileApiVersion) map { sessionId =>
Redirect(routes.User.show(user.username)) withCookies LilaCookie.session("sessionId", sessionId)
} recoverWith authRecovery
}
private def noTorResponse(implicit ctx: Context) = negotiate(
html = Unauthorized(html.auth.tor()).fuccess,
api = _ => Unauthorized(jsonError("Can't login from Tor, sorry!")).fuccess)
def setFingerprint(fp: String, ms: Int) = Auth { ctx =>
me =>
api.setFingerprint(ctx.req, fp) flatMap {
_ ?? { hash =>
!me.lame ?? {
api.recentUserIdsByFingerprint(hash).map(_.filter(me.id!=)) flatMap {
case otherIds if otherIds.size >= 2 => UserRepo countEngines otherIds flatMap {
case nb if nb >= 2 && nb >= otherIds.size / 2 => Env.report.api.autoCheatPrintReport(me.id)
case _ => funit
}
case _ => funit
}
}
}
} inject Ok
}
def passwordReset = Open { implicit ctx =>
forms.passwordResetWithCaptcha map {
case (form, captcha) => Ok(html.auth.passwordReset(form, captcha))
}
}
def passwordResetApply = OpenBody { implicit ctx =>
implicit val req = ctx.body
forms.passwordReset.bindFromRequest.fold(
err => forms.anyCaptcha map { captcha =>
BadRequest(html.auth.passwordReset(err, captcha, false.some))
},
data => {
val email = env.emailAddress.validate(data.email) | data.email
UserRepo enabledByEmail email flatMap {
case Some(user) =>
Env.security.passwordReset.send(user, email) inject Redirect(routes.Auth.passwordResetSent(data.email))
case _ => forms.passwordResetWithCaptcha map {
case (form, captcha) => BadRequest(html.auth.passwordReset(form, captcha, false.some))
}
}
}
)
}
def passwordResetSent(email: String) = Open { implicit ctx =>
fuccess {
Ok(html.auth.passwordResetSent(email))
}
}
def passwordResetConfirm(token: String) = Open { implicit ctx =>
Env.security.passwordReset confirm token flatMap {
case Some(user) =>
fuccess(html.auth.passwordResetConfirm(user, token, forms.passwdReset, none))
case _ => notFound
}
}
def passwordResetConfirmApply(token: String) = OpenBody { implicit ctx =>
Env.security.passwordReset confirm token flatMap {
case Some(user) =>
implicit val req = ctx.body
FormFuResult(forms.passwdReset) { err =>
fuccess(html.auth.passwordResetConfirm(user, token, err, false.some))
} { data =>
UserRepo.passwd(user.id, data.newPasswd1) >> authenticateUser(user)
}
case _ => notFound
}
}
}