-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
EmailChange.scala
81 lines (70 loc) · 2.52 KB
/
EmailChange.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package lila.security
import scalatags.Text.all.*
import lila.core.config.*
import scalalib.Iso
import lila.core.i18n.I18nKey.emails as trans
import lila.mailer.Mailer
import lila.user.{ Me, User, UserRepo }
final class EmailChange(
userRepo: UserRepo,
mailer: Mailer,
baseUrl: BaseUrl,
tokenerSecret: Secret
)(using Executor, lila.core.i18n.Translator):
import Mailer.html.*
def send(user: User, email: EmailAddress): Funit =
(!email.looksLikeFakeEmail).so {
tokener.make(TokenPayload(user.id, email).some).flatMap { token =>
lila.mon.email.send.change.increment()
given play.api.i18n.Lang = user.realLang | lila.core.i18n.defaultLang
val url = s"$baseUrl/account/email/confirm/$token"
lila.log("auth").info(s"Change email URL ${user.username} $email $url")
mailer.send(
Mailer.Message(
to = email,
subject = trans.emailChange_subject.txt(user.username),
text = Mailer.txt.addServiceNote(s"""
${trans.emailChange_intro.txt()}
${trans.emailChange_click.txt()}
$url
${trans.common_orPaste.txt()}
"""),
htmlBody = emailMessage(
pDesc(trans.emailChange_intro()),
p(trans.emailChange_click()),
potentialAction(metaName("Change email address"), Mailer.html.url(url)),
serviceNote
).some
)
)
}
}
// also returns the previous email address
def confirm(token: String): Fu[Option[(Me, Option[EmailAddress])]] =
tokener.read(token).dmap(_.flatten).flatMapz { case TokenPayload(userId, email) =>
for
previous <- userRepo.email(userId)
_ <- userRepo.setEmail(userId, email).recoverDefault
me <- userRepo.me(userId)
yield
logger.info(s"Change email for $userId: $previous -> $email")
me.map(_ -> previous)
}
case class TokenPayload(userId: UserId, email: EmailAddress)
private given Iso.StringIso[Option[TokenPayload]] with
private val sep = ' '
val from = str =>
str.split(sep) match
case Array(id, email) => EmailAddress.from(email).map { TokenPayload(UserId(id), _) }
case _ => none
val to = a =>
a.so { case TokenPayload(userId, email) =>
s"$userId$sep$email"
}
private val tokener = new StringToken[Option[TokenPayload]](
secret = tokenerSecret,
getCurrentValue = p =>
p.so { case TokenPayload(userId, _) =>
userRepo.email(userId).dmap(_.so(_.value))
}
)