Smail/server/controllers/settings_test.go

119 lines
3.8 KiB
Go

package controllers
import (
"encoding/json"
"git.pcux.cn/xuans/Smail/config"
"git.pcux.cn/xuans/Smail/db"
"git.pcux.cn/xuans/Smail/models"
"git.pcux.cn/xuans/Smail/services/auth"
"git.pcux.cn/xuans/Smail/session"
"git.pcux.cn/xuans/Smail/utils/context"
"git.pcux.cn/xuans/Smail/utils/password"
"github.com/alexedwards/scs/v2"
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"testing"
)
func TestModifyPasswordRequiresCurrentPassword(t *testing.T) {
oldConfig := config.Instance
oldDB := db.Instance
oldLoginPairAttempts := loginPairAttempts
oldLoginAccountAttempts := loginAccountAttempts
oldLoginIPAttempts := loginIPAttempts
oldSession := session.Instance
loginPairAttempts = newLoginLimiter(loginFailureLimit)
loginAccountAttempts = newLoginLimiter(loginAccountFailureLimit)
loginIPAttempts = newLoginLimiter(loginIPFailureLimit)
session.Instance = scs.New()
testRoot := t.TempDir()
t.Cleanup(func() {
if db.Instance != nil && db.Instance != oldDB {
_ = db.Instance.Close()
}
config.Instance = oldConfig
db.Instance = oldDB
loginPairAttempts = oldLoginPairAttempts
loginAccountAttempts = oldLoginAccountAttempts
loginIPAttempts = oldLoginIPAttempts
session.Instance = oldSession
})
config.Instance = &config.Config{
DbType: config.DBTypeSQLite,
DbDSN: filepath.Join(testRoot, "settings.db"),
}
if err := db.Init(""); err != nil {
t.Fatal(err)
}
user := models.User{Account: "password-change-user", Name: "密码修改用户"}
if err := auth.SetPassword(&user, "CorrectP12"); err != nil {
t.Fatal(err)
}
if _, err := db.Instance.Insert(&user); err != nil {
t.Fatal(err)
}
originalHash := user.Password
ctx := &context.Context{UserID: user.ID, UserAccount: user.Account, Lang: "zhCn"}
request := httptest.NewRequest(http.MethodPost, "/api/settings/modify_password", strings.NewReader(`{"current_password":"WrongPass123","password":"NewPass123"}`))
recorder := httptest.NewRecorder()
ModifyPassword(ctx, recorder, request)
var failed struct {
ErrorNo int `json:"errorNo"`
}
if err := json.Unmarshal(recorder.Body.Bytes(), &failed); err != nil {
t.Fatal(err)
}
if failed.ErrorNo == 0 {
t.Fatal("password change succeeded with an incorrect current password")
}
var unchanged models.User
unchanged = models.User{}
if _, err := db.Instance.ID(user.ID).Get(&unchanged); err != nil {
t.Fatal(err)
}
if unchanged.Password != originalHash || !password.Verify(unchanged.Password, "CorrectP12") {
t.Fatal("password was changed after current password verification failed")
}
request = httptest.NewRequest(http.MethodPost, "/api/settings/modify_password", strings.NewReader(`{"current_password":"CorrectP12","password":"NewPass123"}`))
loadedContext, err := session.Instance.Load(request.Context(), "")
if err != nil {
t.Fatal(err)
}
request = request.WithContext(loadedContext)
recorder = httptest.NewRecorder()
ModifyPassword(ctx, recorder, request)
var succeeded struct {
ErrorNo int `json:"errorNo"`
}
if err := json.Unmarshal(recorder.Body.Bytes(), &succeeded); err != nil {
t.Fatal(err)
}
if succeeded.ErrorNo != 0 {
t.Fatalf("password change with the correct current password failed: %s", recorder.Body.String())
}
unchanged = models.User{}
if _, err := db.Instance.ID(user.ID).Get(&unchanged); err != nil {
t.Fatal(err)
}
if !password.Verify(unchanged.Password, "NewPass123") {
t.Fatal("password change did not persist the new password")
}
identity, err := session.GetUser(request.Context())
if err != nil {
t.Fatal(err)
}
if identity.ID != 0 {
t.Fatalf("current session was not destroyed after password change: %+v", identity)
}
if unchanged.MustChangePassword != 0 || unchanged.SecurityVersion <= 0 || unchanged.PasswordChangedAt == nil {
t.Fatalf("password lifecycle fields were not updated: %+v", unchanged)
}
}