From a120734bb1d926ecb8f05fdedcd0a600e01ab3c4 Mon Sep 17 00:00:00 2001 From: DacongDA Date: Sat, 12 Jul 2025 00:18:56 +0800 Subject: [PATCH] feat: support links in email to reset password (#3939) --- controllers/service.go | 3 +++ controllers/verification.go | 2 +- object/verification.go | 27 ++++++++++++++++++++++++++- web/src/ProviderEditPage.js | 2 +- web/src/Setting.js | 5 +++++ web/src/auth/ForgetPage.js | 26 +++++++++++++++++++++++--- 6 files changed, 59 insertions(+), 6 deletions(-) diff --git a/controllers/service.go b/controllers/service.go index 4e7674c0..47d5e8d7 100644 --- a/controllers/service.go +++ b/controllers/service.go @@ -140,6 +140,9 @@ func (c *ApiController) SendEmail() { } content = strings.Replace(content, "%{user.friendlyName}", userString, 1) + matchContent := object.ResetLinkReg.Find([]byte(content)) + content = strings.Replace(content, string(matchContent), "", -1) + for _, receiver := range emailForm.Receivers { err = object.SendEmail(provider, emailForm.Title, content, receiver, emailForm.Sender) if err != nil { diff --git a/controllers/verification.go b/controllers/verification.go index d5247890..e5a42b77 100644 --- a/controllers/verification.go +++ b/controllers/verification.go @@ -258,7 +258,7 @@ func (c *ApiController) SendVerificationCode() { return } - sendResp = object.SendVerificationCodeToEmail(organization, user, provider, clientIp, vform.Dest) + sendResp = object.SendVerificationCodeToEmail(organization, user, provider, clientIp, vform.Dest, vform.Method, c.Ctx.Request.Host, application.Name) case object.VerifyTypePhone: if vform.Method == LoginVerification || vform.Method == ForgetVerification { if user != nil && util.GetMaskedPhone(user.Phone) == vform.Dest { diff --git a/object/verification.go b/object/verification.go index 62c67eac..371f3580 100644 --- a/object/verification.go +++ b/object/verification.go @@ -19,6 +19,8 @@ import ( "fmt" "math" "math/rand" + "net/url" + "regexp" "strings" "time" @@ -33,6 +35,8 @@ type VerifyResult struct { Msg string } +var ResetLinkReg *regexp.Regexp + const ( VerificationSuccess = iota wrongCodeError @@ -45,6 +49,10 @@ const ( VerifyTypeEmail = "email" ) +func init() { + ResetLinkReg = regexp.MustCompile("(?s)(.*?)") +} + type VerificationRecord struct { Owner string `xorm:"varchar(100) notnull pk" json:"owner"` Name string `xorm:"varchar(100) notnull pk" json:"name"` @@ -81,7 +89,7 @@ func IsAllowSend(user *User, remoteAddr, recordType string) error { return nil } -func SendVerificationCodeToEmail(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error { +func SendVerificationCodeToEmail(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string, method string, host string, applicationName string) error { sender := organization.DisplayName title := provider.Title @@ -93,6 +101,23 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide // "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes." content := strings.Replace(provider.Content, "%s", code, 1) + if method == "forget" { + originFrontend, _ := getOriginFromHost(host) + + query := url.Values{} + query.Add("code", code) + query.Add("username", user.Name) + query.Add("dest", util.GetMaskedEmail(dest)) + forgetURL := originFrontend + "/forget/" + applicationName + "?" + query.Encode() + + content = strings.Replace(content, "%link", forgetURL, -1) + content = strings.Replace(content, "", "", -1) + content = strings.Replace(content, "", "", -1) + } else { + matchContent := ResetLinkReg.Find([]byte(content)) + content = strings.Replace(content, string(matchContent), "", -1) + } + userString := "Hi" if user != nil { userString = user.GetFriendlyName() diff --git a/web/src/ProviderEditPage.js b/web/src/ProviderEditPage.js index 6e9762a7..d066d8ef 100644 --- a/web/src/ProviderEditPage.js +++ b/web/src/ProviderEditPage.js @@ -1227,7 +1227,7 @@ class ProviderEditPage extends React.Component { -