Skip to content

Commit 2aa5c99

Browse files
committed
fix
1 parent 328d908 commit 2aa5c99

File tree

4 files changed

+135
-90
lines changed

4 files changed

+135
-90
lines changed

routers/web/auth/auth.go

+61-56
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package auth
77
import (
88
"errors"
99
"fmt"
10+
"html/template"
1011
"net/http"
1112
"strings"
1213

@@ -37,12 +38,10 @@ import (
3738
)
3839

3940
const (
40-
// tplSignIn template for sign in page
41-
tplSignIn base.TplName = "user/auth/signin"
42-
// tplSignUp template path for sign up page
43-
tplSignUp base.TplName = "user/auth/signup"
44-
// TplActivate template path for activate user
45-
TplActivate base.TplName = "user/auth/activate"
41+
tplSignIn base.TplName = "user/auth/signin" // for sign in page
42+
tplSignUp base.TplName = "user/auth/signup" // for sign up page
43+
TplActivate base.TplName = "user/auth/activate" // for activate user
44+
TplActivatePrompt base.TplName = "user/auth/activate_prompt" // for showing a message for user activation
4645
)
4746

4847
// autoSignIn reads cookie and try to auto-login.
@@ -613,71 +612,77 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
613612
}
614613
}
615614

616-
// Send confirmation email
617-
if !u.IsActive && u.ID > 1 {
618-
if setting.Service.RegisterManualConfirm {
619-
ctx.Data["ManualActivationOnly"] = true
620-
ctx.HTML(http.StatusOK, TplActivate)
621-
return false
622-
}
615+
// for active user or the first (admin) user, we don't need to send confirmation email
616+
if u.IsActive || u.ID == 1 {
617+
return true
618+
}
623619

624-
mailer.SendActivateAccountMail(ctx.Locale, u)
620+
if setting.Service.RegisterManualConfirm {
621+
renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.manual_activation_only"))
622+
return false
623+
}
625624

626-
ctx.Data["IsSendRegisterMail"] = true
627-
ctx.Data["Email"] = u.Email
628-
ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
629-
ctx.HTML(http.StatusOK, TplActivate)
625+
sendActivateEmail(ctx, u)
626+
return false
627+
}
630628

631-
if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
632-
log.Error("Set cache(MailResendLimit) fail: %v", err)
633-
}
634-
return false
629+
func renderActivationPromptMessage(ctx *context.Context, msg template.HTML) {
630+
ctx.Data["ActivationPromptMessage"] = msg
631+
ctx.HTML(http.StatusOK, TplActivatePrompt)
632+
}
633+
634+
func sendActivateEmail(ctx *context.Context, u *user_model.User) {
635+
if ctx.Cache.IsExist("MailResendLimit_" + u.LowerName) {
636+
renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.resent_limit_prompt"))
637+
return
635638
}
636639

637-
return true
640+
if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
641+
log.Error("Set cache(MailResendLimit) fail: %v", err)
642+
renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.resent_limit_prompt"))
643+
return
644+
}
645+
646+
mailer.SendActivateAccountMail(ctx.Locale, u)
647+
648+
activeCodeLives := timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
649+
msgHTML := ctx.Locale.Tr("auth.confirmation_mail_sent_prompt", u.Email, activeCodeLives)
650+
renderActivationPromptMessage(ctx, msgHTML)
638651
}
639652

640653
// Activate render activate user page
641654
func Activate(ctx *context.Context) {
642655
code := ctx.FormString("code")
643656

644-
if len(code) == 0 {
645-
ctx.Data["IsActivatePage"] = true
646-
if ctx.Doer == nil || ctx.Doer.IsActive {
647-
ctx.NotFound("invalid user", nil)
657+
if code == "" {
658+
if ctx.Doer == nil {
659+
ctx.Redirect(setting.AppSubURL + "/user/login")
660+
return
661+
} else if ctx.Doer.IsActive {
662+
ctx.Redirect(setting.AppSubURL + "/")
648663
return
649664
}
650-
// Resend confirmation email.
651-
if setting.Service.RegisterEmailConfirm {
652-
if ctx.Cache.IsExist("MailResendLimit_" + ctx.Doer.LowerName) {
653-
ctx.Data["ResendLimited"] = true
654-
} else {
655-
ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
656-
mailer.SendActivateAccountMail(ctx.Locale, ctx.Doer)
657665

658-
if err := ctx.Cache.Put("MailResendLimit_"+ctx.Doer.LowerName, ctx.Doer.LowerName, 180); err != nil {
659-
log.Error("Set cache(MailResendLimit) fail: %v", err)
660-
}
661-
}
662-
} else {
663-
ctx.Data["ServiceNotEnabled"] = true
666+
if setting.MailService == nil || !setting.Service.RegisterEmailConfirm {
667+
renderActivationPromptMessage(ctx, ctx.Tr("auth.disable_register_mail"))
668+
return
664669
}
665-
ctx.HTML(http.StatusOK, TplActivate)
670+
671+
// Resend confirmation email.
672+
sendActivateEmail(ctx, ctx.Doer)
666673
return
667674
}
668675

669676
user := user_model.VerifyUserActiveCode(ctx, code)
670-
// if code is wrong
671-
if user == nil {
672-
ctx.Data["IsCodeInvalid"] = true
673-
ctx.HTML(http.StatusOK, TplActivate)
677+
if user == nil { // if code is wrong
678+
renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.invalid_code"))
674679
return
675680
}
676681

677682
// if account is local account, verify password
678683
if user.LoginSource == 0 {
679-
ctx.Data["Code"] = code
680-
ctx.Data["NeedsPassword"] = true
684+
ctx.Data["ActivationCode"] = code
685+
ctx.Data["NeedVerifyLocalPassword"] = true
681686
ctx.HTML(http.StatusOK, TplActivate)
682687
return
683688
}
@@ -688,30 +693,30 @@ func Activate(ctx *context.Context) {
688693
// ActivatePost handles account activation with password check
689694
func ActivatePost(ctx *context.Context) {
690695
code := ctx.FormString("code")
691-
if len(code) == 0 {
696+
if code == "" || ctx.Doer == nil || ctx.Doer.IsActive {
692697
ctx.Redirect(setting.AppSubURL + "/user/activate")
693698
return
694699
}
695700

696701
user := user_model.VerifyUserActiveCode(ctx, code)
697-
// if code is wrong
698-
if user == nil {
699-
ctx.Data["IsCodeInvalid"] = true
700-
ctx.HTML(http.StatusOK, TplActivate)
702+
if user == nil { // if code is wrong
703+
renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.invalid_code"))
701704
return
702705
}
703706

704707
// if account is local account, verify password
705708
if user.LoginSource == 0 {
706709
password := ctx.FormString("password")
707-
if len(password) == 0 {
708-
ctx.Data["Code"] = code
709-
ctx.Data["NeedsPassword"] = true
710+
if password == "" {
711+
ctx.Data["ActivationCode"] = code
712+
ctx.Data["NeedVerifyLocalPassword"] = true
710713
ctx.HTML(http.StatusOK, TplActivate)
711714
return
712715
}
713716
if !user.ValidatePassword(password) {
714-
ctx.Data["IsPasswordInvalid"] = true
717+
ctx.Flash.Error(ctx.Locale.Tr("auth.invalid_password"), true)
718+
ctx.Data["ActivationCode"] = code
719+
ctx.Data["NeedVerifyLocalPassword"] = true
715720
ctx.HTML(http.StatusOK, TplActivate)
716721
return
717722
}

templates/user/auth/activate.tmpl

+15-33
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,22 @@
99
</h2>
1010
<div class="ui attached segment">
1111
{{template "base/alert" .}}
12-
{{if .IsActivatePage}}
13-
{{if .ServiceNotEnabled}}
14-
<p class="center">{{ctx.Locale.Tr "auth.disable_register_mail"}}</p>
15-
{{else if .ResendLimited}}
16-
<p class="center">{{ctx.Locale.Tr "auth.resent_limit_prompt"}}</p>
17-
{{else}}
18-
<p>{{ctx.Locale.Tr "auth.confirmation_mail_sent_prompt" (.SignedUser.Email|Escape) .ActiveCodeLives}}</p>
19-
{{end}}
12+
{{if .NeedVerifyLocalPassword}}
13+
<div class="required inline field">
14+
<label for="verify-password">{{ctx.Locale.Tr "password"}}</label>
15+
<input id="verify-password" name="password" type="password" autocomplete="off" required>
16+
</div>
17+
<div class="inline field">
18+
<label></label>
19+
<button class="ui primary button">{{ctx.Locale.Tr "install.confirm_password"}}</button>
20+
</div>
21+
<input name="code" type="hidden" value="{{.ActivationCode}}">
2022
{{else}}
21-
{{if .NeedsPassword}}
22-
<div class="required inline field">
23-
<label for="password">{{ctx.Locale.Tr "password"}}</label>
24-
<input id="password" name="password" type="password" autocomplete="off" required>
25-
</div>
26-
<div class="inline field">
27-
<label></label>
28-
<button class="ui primary button">{{ctx.Locale.Tr "install.confirm_password"}}</button>
29-
</div>
30-
<input id="code" name="code" type="hidden" value="{{.Code}}">
31-
{{else if .IsSendRegisterMail}}
32-
<p>{{ctx.Locale.Tr "auth.confirmation_mail_sent_prompt" (.Email|Escape) .ActiveCodeLives}}</p>
33-
{{else if .IsCodeInvalid}}
34-
<p>{{ctx.Locale.Tr "auth.invalid_code"}}</p>
35-
{{else if .IsPasswordInvalid}}
36-
<p>{{ctx.Locale.Tr "auth.invalid_password"}}</p>
37-
{{else if .ManualActivationOnly}}
38-
<p class="center">{{ctx.Locale.Tr "auth.manual_activation_only"}}</p>
39-
{{else}}
40-
<p>{{ctx.Locale.Tr "auth.has_unconfirmed_mail" (.SignedUser.Name|Escape) (.SignedUser.Email|Escape)}}</p>
41-
<div class="divider"></div>
42-
<div class="text right">
43-
<button class="ui primary button">{{ctx.Locale.Tr "auth.resend_mail"}}</button>
44-
</div>
45-
{{end}}
23+
<p>{{ctx.Locale.Tr "auth.has_unconfirmed_mail" .SignedUser.Name .SignedUser.Email}}</p>
24+
<div class="divider"></div>
25+
<div class="text right">
26+
<button class="ui primary button">{{ctx.Locale.Tr "auth.resend_mail"}}</button>
27+
</div>
4628
{{end}}
4729
</div>
4830
</form>
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{{template "base/head" .}}
2+
<div role="main" aria-label="{{.Title}}" class="page-content user activate">
3+
<div class="ui middle very relaxed page grid">
4+
<div class="column">
5+
<h2 class="ui top attached header">
6+
{{ctx.Locale.Tr "auth.active_your_account"}}
7+
</h2>
8+
<div class="ui attached segment">
9+
{{template "base/alert" .}}
10+
<p>{{.ActivationPromptMessage}}</p>
11+
</div>
12+
</div>
13+
</div>
14+
</div>
15+
{{template "base/footer" .}}

tests/integration/signup_test.go

+44-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"code.gitea.io/gitea/models/unittest"
1313
user_model "code.gitea.io/gitea/models/user"
1414
"code.gitea.io/gitea/modules/setting"
15+
"code.gitea.io/gitea/modules/test"
1516
"code.gitea.io/gitea/modules/translation"
1617
"code.gitea.io/gitea/tests"
1718

@@ -58,7 +59,7 @@ func TestSignupAsRestricted(t *testing.T) {
5859
assert.True(t, user2.IsRestricted)
5960
}
6061

61-
func TestSignupEmail(t *testing.T) {
62+
func TestSignupEmailValidation(t *testing.T) {
6263
defer tests.PrepareTestEnv(t)()
6364

6465
setting.Service.EnableCaptcha = false
@@ -91,3 +92,45 @@ func TestSignupEmail(t *testing.T) {
9192
}
9293
}
9394
}
95+
96+
func TestSignupEmailActive(t *testing.T) {
97+
defer tests.PrepareTestEnv(t)()
98+
defer test.MockVariableValue(&setting.Service.RegisterEmailConfirm, true)()
99+
100+
req := NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
101+
"user_name": "test-user-1",
102+
"email": "[email protected]",
103+
"password": "password1",
104+
"retype": "password1",
105+
})
106+
resp := MakeRequest(t, req, http.StatusOK)
107+
assert.Contains(t, resp.Body.String(), `A new confirmation email has been sent to <b>[email protected]</b>.`)
108+
109+
session := loginUserWithPassword(t, "test-user-1", "password1")
110+
resp = session.MakeRequest(t, NewRequest(t, "GET", "/user/activate"), http.StatusOK)
111+
assert.Contains(t, resp.Body.String(), "You have already requested an activation email recently")
112+
113+
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "test-user-1"})
114+
activationCode := user.GenerateEmailActivateCode(user.Email)
115+
resp = session.MakeRequest(t, NewRequest(t, "GET", "/user/activate?code="+activationCode), http.StatusOK)
116+
assert.Contains(t, resp.Body.String(), `<input id="verify-password"`)
117+
118+
req = NewRequestWithValues(t, "POST", "/user/activate", map[string]string{
119+
"code": activationCode,
120+
"password": "password-wrong",
121+
})
122+
resp = session.MakeRequest(t, req, http.StatusOK)
123+
assert.Contains(t, resp.Body.String(), `Your password does not match`)
124+
assert.Contains(t, resp.Body.String(), `<input id="verify-password"`)
125+
126+
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "test-user-1"})
127+
assert.False(t, user.IsActive)
128+
req = NewRequestWithValues(t, "POST", "/user/activate", map[string]string{
129+
"code": activationCode,
130+
"password": "password1",
131+
})
132+
resp = session.MakeRequest(t, req, http.StatusSeeOther)
133+
assert.Equal(t, "/", test.RedirectURL(resp))
134+
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "test-user-1"})
135+
assert.True(t, user.IsActive)
136+
}

0 commit comments

Comments
 (0)