Skip to content

Commit a115f2b

Browse files
committed
fix account email validation and inactive reset
1 parent 963b512 commit a115f2b

4 files changed

Lines changed: 146 additions & 5 deletions

File tree

apis/account.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ func Register(c *fiber.Ctx) (err error) {
3737
if err != nil {
3838
return err
3939
}
40+
if err = validateAccountEmail(body.Email); err != nil {
41+
return err
42+
}
4043

4144
// check verification code
4245
ok := auth.CheckVerificationCode(body.Email, scope, string(body.Verification))
@@ -116,6 +119,11 @@ func RegisterDebugInBatch(c *fiber.Ctx) (err error) {
116119
if err != nil {
117120
return err
118121
}
122+
for _, data := range body.Data {
123+
if err := validateAccountEmail(data.Email); err != nil {
124+
return err
125+
}
126+
}
119127

120128
// check registered
121129
var emailHashes []string
@@ -276,6 +284,10 @@ func RegisterDebugInBatch(c *fiber.Ctx) (err error) {
276284
}
277285

278286
func register(c *fiber.Ctx, email, password string, batch bool) error {
287+
if err := validateAccountEmail(email); err != nil {
288+
return err
289+
}
290+
279291
registered, err := HasRegisteredEmail(DB, email)
280292
if err != nil {
281293
return err
@@ -365,6 +377,9 @@ func ChangePassword(c *fiber.Ctx) error {
365377
if err != nil {
366378
return err
367379
}
380+
if err = validateAccountEmail(body.Email); err != nil {
381+
return err
382+
}
368383

369384
registered, err := HasRegisteredEmail(DB, body.Email)
370385
if err != nil {
@@ -397,7 +412,10 @@ func ChangePassword(c *fiber.Ctx) error {
397412
return err
398413
}
399414

400-
user.IsActive = true
415+
if !user.IsActive {
416+
return common.Forbidden("账号已停用")
417+
}
418+
401419
user.Password, err = auth.MakePassword(body.Password)
402420
if err != nil {
403421
return err
@@ -473,10 +491,7 @@ func VerifyWithEmail(c *fiber.Ctx) error {
473491
}
474492

475493
func verifyWithEmail(c *fiber.Ctx, email string, check bool) error {
476-
if !utils.ValidateEmail(email) {
477-
return common.BadRequest("email invalid")
478-
}
479-
err := utils.ValidateEmailFudan(email)
494+
err := validateAccountEmail(email)
480495
if err != nil {
481496
return err
482497
}
@@ -542,6 +557,13 @@ func verifyWithEmail(c *fiber.Ctx, email string, check bool) error {
542557
})
543558
}
544559

560+
func validateAccountEmail(email string) error {
561+
if !utils.ValidateEmail(email) {
562+
return common.BadRequest("email invalid")
563+
}
564+
return utils.ValidateEmailFudan(email)
565+
}
566+
545567
// VerifyWithApikey godoc
546568
//
547569
// @Summary verify with email in query and apikey

apis/account_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package apis
2+
3+
import (
4+
"bytes"
5+
"database/sql"
6+
"fmt"
7+
"net/http"
8+
"net/http/httptest"
9+
"testing"
10+
11+
"github.com/gofiber/fiber/v2"
12+
"github.com/opentreehole/go-common"
13+
"gorm.io/driver/sqlite"
14+
"gorm.io/gorm"
15+
"gorm.io/gorm/schema"
16+
17+
"auth_next/config"
18+
"auth_next/models"
19+
"auth_next/utils/auth"
20+
)
21+
22+
func TestChangePasswordRejectsInactiveUser(t *testing.T) {
23+
oldDB := models.DB
24+
oldStandalone := config.Config.Standalone
25+
oldShamirFeature := config.Config.ShamirFeature
26+
oldEmailWhitelist := config.Config.EmailWhitelist
27+
oldVerificationCodeExpires := config.Config.VerificationCodeExpires
28+
t.Cleanup(func() {
29+
models.DB = oldDB
30+
config.Config.Standalone = oldStandalone
31+
config.Config.ShamirFeature = oldShamirFeature
32+
config.Config.EmailWhitelist = oldEmailWhitelist
33+
config.Config.VerificationCodeExpires = oldVerificationCodeExpires
34+
})
35+
36+
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{
37+
NamingStrategy: schema.NamingStrategy{SingularTable: true},
38+
})
39+
if err != nil {
40+
t.Fatal(err)
41+
}
42+
sqlDB, err := db.DB()
43+
if err != nil {
44+
t.Fatal(err)
45+
}
46+
t.Cleanup(func() {
47+
_ = sqlDB.Close()
48+
})
49+
if err := db.AutoMigrate(models.User{}, models.DeleteIdentifier{}, models.UserJwtSecret{}); err != nil {
50+
t.Fatal(err)
51+
}
52+
53+
models.DB = db
54+
config.Config.Standalone = true
55+
config.Config.ShamirFeature = false
56+
config.Config.EmailWhitelist = nil
57+
config.Config.VerificationCodeExpires = 5
58+
auth.InitVerificationCodeCache()
59+
60+
const email = "inactive@example.com"
61+
oldPassword, err := auth.MakePassword("old-password")
62+
if err != nil {
63+
t.Fatal(err)
64+
}
65+
user := models.User{
66+
Identifier: sql.NullString{String: auth.MakeIdentifier(email), Valid: true},
67+
Password: oldPassword,
68+
}
69+
if err := db.Create(&user).Error; err != nil {
70+
t.Fatal(err)
71+
}
72+
if err := db.Model(&user).Update("is_active", false).Error; err != nil {
73+
t.Fatal(err)
74+
}
75+
76+
code, err := auth.SetVerificationCode(email, "reset")
77+
if err != nil {
78+
t.Fatal(err)
79+
}
80+
81+
app := fiber.New(fiber.Config{ErrorHandler: common.ErrorHandler})
82+
app.Put("/register", ChangePassword)
83+
84+
body := fmt.Sprintf(
85+
`{"email":%q,"password":"new-password","verification":%q}`,
86+
email,
87+
code,
88+
)
89+
req := httptest.NewRequest(http.MethodPut, "/register", bytes.NewBufferString(body))
90+
req.Header.Set("Content-Type", "application/json")
91+
resp, err := app.Test(req)
92+
if err != nil {
93+
t.Fatal(err)
94+
}
95+
if resp.StatusCode != http.StatusForbidden {
96+
t.Fatalf("expected status %d, got %d", http.StatusForbidden, resp.StatusCode)
97+
}
98+
99+
var updated models.User
100+
if err := db.Take(&updated, user.ID).Error; err != nil {
101+
t.Fatal(err)
102+
}
103+
if updated.IsActive {
104+
t.Fatal("expected inactive user to remain inactive after password reset attempt")
105+
}
106+
ok, err := auth.CheckPassword("new-password", updated.Password)
107+
if err != nil {
108+
t.Fatal(err)
109+
}
110+
if ok {
111+
t.Fatal("expected inactive user's password not to be changed")
112+
}
113+
}

utils/validate.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"golang.org/x/exp/slices"
55
"strconv"
66
"strings"
7+
"unicode"
78

89
"github.com/go-playground/validator/v10"
910
"github.com/opentreehole/go-common"
@@ -20,6 +21,10 @@ func init() {
2021
}
2122

2223
func ValidateEmail(email string) bool {
24+
if strings.ContainsFunc(email, unicode.IsSpace) {
25+
return false
26+
}
27+
2328
if err := common.Validate.Var(email, "required,email"); err != nil {
2429
return false
2530
}

utils/validate_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func TestValidateEmail(t *testing.T) {
1818
assert.Equal(t, ValidateEmail(" abcd@fudan.edu.cn"), false)
1919
assert.Equal(t, ValidateEmail("abcd @fudan.edu.cn"), false)
2020
assert.Equal(t, ValidateEmail("\tabcd@fudan.edu.cn"), false)
21+
assert.Equal(t, ValidateEmail("\"abc def\"@fudan.edu.cn"), false)
2122
}
2223

2324
func TestValidateEmailFudan(t *testing.T) {

0 commit comments

Comments
 (0)