feat: 实现 Admin 采购员账号管理 (#51)
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cmautobuy/admin/repository"
|
||||
"cmautobuy/admin/service"
|
||||
)
|
||||
|
||||
func Test用户管理管理员与采购员权限边界(t *testing.T) {
|
||||
db, err := repository.Open(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
if err := repository.Migrate(db); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
now := time.Now()
|
||||
if err := service.SetupInitialAdmin(db, "admin", "admin-password", "admin-password", now); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
admin, _ := repository.FindUserByUsername(db, "admin")
|
||||
if err := service.CreatePurchaser(db, admin, "buyer", "buyer-password", "buyer-password", now); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
router, err := newRouter(db)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
adminToken, _, _, err := service.Login(db, "admin", "admin-password", now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
adminPageRequest := httptest.NewRequest(http.MethodGet, "/users", nil)
|
||||
adminPageRequest.AddCookie(&http.Cookie{Name: "cmautobuy_session", Value: adminToken})
|
||||
adminPage := httptest.NewRecorder()
|
||||
router.ServeHTTP(adminPage, adminPageRequest)
|
||||
if adminPage.Code != http.StatusOK {
|
||||
t.Fatalf("管理员 GET /users = %d,响应:%s", adminPage.Code, adminPage.Body.String())
|
||||
}
|
||||
for _, want := range []string{"用户管理", "新增采购员", "buyer", "重置密码", "data-confirm-submit"} {
|
||||
if !strings.Contains(adminPage.Body.String(), want) {
|
||||
t.Errorf("用户管理页缺少 %q", want)
|
||||
}
|
||||
}
|
||||
for _, secret := range []string{"admin-password", "buyer-password"} {
|
||||
if strings.Contains(adminPage.Body.String(), secret) {
|
||||
t.Fatalf("用户管理页泄露密码 %q", secret)
|
||||
}
|
||||
}
|
||||
csrfCookie := findResponseCookie(t, adminPage, "cmautobuy_csrf")
|
||||
|
||||
duplicate := postFormRequest("/users/create", url.Values{
|
||||
"csrf_token": {csrfCookie.Value}, "username": {"BUYER"},
|
||||
"password": {"another-password"}, "password_confirm": {"another-password"},
|
||||
}, csrfCookie)
|
||||
duplicate.AddCookie(&http.Cookie{Name: "cmautobuy_session", Value: adminToken})
|
||||
duplicateResponse := httptest.NewRecorder()
|
||||
router.ServeHTTP(duplicateResponse, duplicate)
|
||||
if duplicateResponse.Code != http.StatusBadRequest ||
|
||||
!strings.Contains(duplicateResponse.Body.String(), "用户名已经存在") ||
|
||||
!strings.Contains(duplicateResponse.Body.String(), `value="BUYER"`) {
|
||||
t.Fatalf("重复用户名应保留用户名并重开表单: %d %s", duplicateResponse.Code, duplicateResponse.Body.String())
|
||||
}
|
||||
if strings.Contains(duplicateResponse.Body.String(), "another-password") {
|
||||
t.Fatal("校验失败页面不能回显密码")
|
||||
}
|
||||
|
||||
// 即使已登录管理员,没有 CSRF 也不能创建账号。
|
||||
noCSRF := postFormRequest("/users/create", url.Values{
|
||||
"username": {"staff"}, "password": {"staff-password"},
|
||||
"password_confirm": {"staff-password"},
|
||||
})
|
||||
noCSRF.AddCookie(&http.Cookie{Name: "cmautobuy_session", Value: adminToken})
|
||||
noCSRFResponse := httptest.NewRecorder()
|
||||
router.ServeHTTP(noCSRFResponse, noCSRF)
|
||||
if noCSRFResponse.Code != http.StatusForbidden {
|
||||
t.Fatalf("无 CSRF 创建采购员 = %d,期望 403", noCSRFResponse.Code)
|
||||
}
|
||||
|
||||
create := postFormRequest("/users/create", url.Values{
|
||||
"csrf_token": {csrfCookie.Value}, "username": {"staff"},
|
||||
"password": {"staff-password"}, "password_confirm": {"staff-password"},
|
||||
}, csrfCookie)
|
||||
create.AddCookie(&http.Cookie{Name: "cmautobuy_session", Value: adminToken})
|
||||
createResponse := httptest.NewRecorder()
|
||||
router.ServeHTTP(createResponse, create)
|
||||
if createResponse.Code != http.StatusSeeOther || !strings.HasPrefix(createResponse.Header().Get("Location"), "/users?") {
|
||||
t.Fatalf("创建采购员 = %d %s", createResponse.Code, createResponse.Header().Get("Location"))
|
||||
}
|
||||
if _, err := repository.FindUserByUsername(db, "STAFF"); err != nil {
|
||||
t.Fatalf("HTTP 创建的采购员不存在: %v", err)
|
||||
}
|
||||
|
||||
buyerToken, _, _, err := service.Login(db, "buyer", "buyer-password", now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
buyerBusiness := httptest.NewRequest(http.MethodGet, "/shopee", nil)
|
||||
buyerBusiness.AddCookie(&http.Cookie{Name: "cmautobuy_session", Value: buyerToken})
|
||||
buyerBusinessResponse := httptest.NewRecorder()
|
||||
router.ServeHTTP(buyerBusinessResponse, buyerBusiness)
|
||||
if buyerBusinessResponse.Code != http.StatusOK {
|
||||
t.Fatalf("采购员应可访问业务页,实际 %d", buyerBusinessResponse.Code)
|
||||
}
|
||||
if strings.Contains(buyerBusinessResponse.Body.String(), `href="/users"`) {
|
||||
t.Fatal("采购员导航不应显示用户管理入口")
|
||||
}
|
||||
|
||||
buyerUsers := httptest.NewRequest(http.MethodGet, "/users", nil)
|
||||
buyerUsers.AddCookie(&http.Cookie{Name: "cmautobuy_session", Value: buyerToken})
|
||||
buyerUsersResponse := httptest.NewRecorder()
|
||||
router.ServeHTTP(buyerUsersResponse, buyerUsers)
|
||||
if buyerUsersResponse.Code != http.StatusForbidden || !strings.Contains(buyerUsersResponse.Body.String(), "只有管理员") {
|
||||
t.Fatalf("采购员访问 /users = %d,响应:%s", buyerUsersResponse.Code, buyerUsersResponse.Body.String())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user