email unicity validation and mod actions

This commit is contained in:
Thibault Duplessis 2015-10-22 12:07:40 +02:00
parent 41c7f6a9bc
commit 7144283ae3
4 changed files with 29 additions and 22 deletions

View file

@ -67,14 +67,14 @@ object Account extends LilaController {
}
}
private def emailForm(id: String) = UserRepo email id map { email =>
Env.security.forms.changeEmail.fill(
private def emailForm(user: UserModel) = UserRepo email user.id map { email =>
Env.security.forms.changeEmail(user).fill(
lila.security.DataForm.ChangeEmail(~email, ""))
}
def email = Auth { implicit ctx =>
me =>
emailForm(me.id) map { form =>
emailForm(me) map { form =>
Ok(html.account.email(me, form))
}
}
@ -82,14 +82,14 @@ object Account extends LilaController {
def emailApply = AuthBody { implicit ctx =>
me =>
implicit val req = ctx.body
FormFuResult(Env.security.forms.changeEmail) { err =>
FormFuResult(Env.security.forms.changeEmail(me)) { err =>
fuccess(html.account.email(me, err))
} { data =>
val email = Env.security.emailAddress.validate(data.email) err s"Invalid email ${data.email}"
for {
ok UserRepo.checkPassword(me.id, data.passwd)
_ ok ?? UserRepo.email(me.id, email)
form <- emailForm(me.id)
form <- emailForm(me)
} yield {
val content = html.account.email(me, form, ok.some)
ok.fold(Ok(content), BadRequest(content))

View file

@ -70,8 +70,8 @@ object Mod extends LilaController {
implicit def req = ctx.body
OptionFuResult(UserRepo named username) { user =>
if (isGranted(_.SetEmail) && !isGranted(_.SetEmail, user))
Env.security.forms.modEmail.bindFromRequest.fold(
err => fuccess(redirect(user.username, mod = true)),
Env.security.forms.modEmail(user).bindFromRequest.fold(
err => BadRequest(err.toString).fuccess,
email => modApi.setEmail(me.id, user.id, email) inject redirect(user.username, mod = true)
)
else fuccess(authorizationFailed(ctx.req))

View file

@ -7,6 +7,7 @@ import play.api.data.validation.Constraints
import lila.common.LameName
import lila.db.api.$count
import lila.user.tube.userTube
import lila.user.User
final class DataForm(
val captcher: akka.actor.ActorSelection,
@ -27,8 +28,8 @@ final class DataForm(
private val anyEmail = nonEmptyText.verifying(Constraints.emailAddress)
private val acceptableEmail = anyEmail.verifying(emailAddress.acceptableConstraint)
private val uniqueEmail = email.verifying(emailAddress.uniqueConstraint)
private val acceptableUniqueEmail = acceptableEmail.verifying(emailAddress.uniqueConstraint)
private def acceptableUniqueEmail(forUser: Option[User]) =
acceptableEmail.verifying(emailAddress uniqueConstraint forUser)
object signup {
@ -47,7 +48,7 @@ final class DataForm(
val website = Form(mapping(
"username" -> username,
"password" -> text(minLength = 4),
"email" -> acceptableUniqueEmail,
"email" -> acceptableUniqueEmail(none),
"g-recaptcha-response" -> nonEmptyText,
"gameId" -> nonEmptyText,
"move" -> nonEmptyText
@ -57,7 +58,7 @@ final class DataForm(
val mobile = Form(mapping(
"username" -> username,
"password" -> text(minLength = 4),
"email" -> optional(acceptableUniqueEmail)
"email" -> optional(acceptableUniqueEmail(none))
)(MobileSignupData.apply)(_ => None))
def websiteWithCaptcha = withCaptcha(website)
@ -89,14 +90,12 @@ final class DataForm(
_.samePasswords
))
val changeEmail = Form(mapping(
"email" -> acceptableUniqueEmail,
def changeEmail(user: User) = Form(mapping(
"email" -> acceptableUniqueEmail(user.some),
"passwd" -> nonEmptyText
)(ChangeEmail.apply)(ChangeEmail.unapply)
.verifying("This email already exists", e => !emailAddress.isTaken(e.email))
)
)(ChangeEmail.apply)(ChangeEmail.unapply))
val modEmail = Form(single("email" -> acceptableUniqueEmail))
def modEmail(user: User) = Form(single("email" -> acceptableUniqueEmail(user.some)))
val closeAccount = Form(single("passwd" -> nonEmptyText))
}

View file

@ -1,5 +1,7 @@
package lila.security
import lila.user.User
import play.api.data.validation._
/**
@ -34,8 +36,12 @@ final class EmailAddress(disposable: DisposableEmailDomain) {
def isValid(email: String) = validate(email).isDefined
def isTaken(email: String): Boolean = validate(email) ?? { e =>
lila.user.UserRepo.idByEmail(e).await.isDefined
private def isTakenBy(email: String, forUser: Option[User]): Option[String] = validate(email) ?? { e =>
(lila.user.UserRepo.idByEmail(e).await, forUser) match {
case (None, _) => none
case (Some(userId), Some(user)) => userId != user.id option userId
case (someUserId, _) => someUserId
}
}
val acceptableConstraint = Constraint[String]("constraint.email_acceptable") { e =>
@ -43,9 +49,11 @@ final class EmailAddress(disposable: DisposableEmailDomain) {
else Invalid(ValidationError("error.email_acceptable"))
}
val uniqueConstraint = Constraint[String]("constraint.email_unique") { e =>
if (isTaken(e)) Invalid(ValidationError("error.email_unique"))
else Valid
def uniqueConstraint(forUser: Option[User]) = Constraint[String]("constraint.email_unique") { e =>
isTakenBy(e, forUser) match {
case Some(userId) => Invalid(ValidationError(s"Email already in use by $userId"))
case None => Valid
}
}
private def isGmail(domain: String) = domain == "gmail.com"