test: 覆盖管理端系统安全回归 (#69)
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -33,6 +35,20 @@ func TestSystemRouterExposesLoginWithoutCaptcha(t *testing.T) {
|
||||
t.Fatalf("read-only system route must not be registered: %s", path)
|
||||
}
|
||||
}
|
||||
for _, request := range []struct{ method, path string }{
|
||||
{http.MethodPost, "/api/v1/menu"},
|
||||
{http.MethodPut, "/api/v1/menu/1"},
|
||||
{http.MethodDelete, "/api/v1/menu"},
|
||||
{http.MethodPut, "/api/v1/sys-api/1"},
|
||||
{http.MethodDelete, "/api/v1/sys-login-log"},
|
||||
{http.MethodPut, "/api/v1/roledatascope"},
|
||||
} {
|
||||
recorder := httptest.NewRecorder()
|
||||
engine.ServeHTTP(recorder, httptest.NewRequest(request.method, request.path, nil))
|
||||
if recorder.Code != http.StatusNotFound {
|
||||
t.Fatalf("%s %s status = %d, want 404", request.method, request.path, recorder.Code)
|
||||
}
|
||||
}
|
||||
for _, path := range []string{"GET /api/v1/captcha", "GET /api/v1/getCaptcha"} {
|
||||
if _, ok := routes[path]; ok {
|
||||
t.Fatalf("captcha route must not be registered: %s", path)
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
mysqldriver "github.com/go-sql-driver/mysql"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
|
||||
"git.ilapage.cn/OPC/chorus/admin/app/admin/models"
|
||||
"git.ilapage.cn/OPC/chorus/admin/app/admin/service/dto"
|
||||
"git.ilapage.cn/OPC/chorus/admin/common/actions"
|
||||
commonDto "git.ilapage.cn/OPC/chorus/admin/common/dto"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
)
|
||||
|
||||
func TestAdminProtectionMySQL(t *testing.T) {
|
||||
db := openAdminProtectionDB(t)
|
||||
suffix := fmt.Sprint(time.Now().UnixNano())
|
||||
usernames := []string{"protection-current-" + suffix, "protection-other-" + suffix}
|
||||
var role models.SysRole
|
||||
if err := db.Where("role_key = ?", BuiltInOperatorRoleKey).First(&role).Error; err != nil {
|
||||
t.Fatalf("load protected role: %v", err)
|
||||
}
|
||||
users := []models.SysUser{
|
||||
{Username: usernames[0], NickName: "Synthetic current", RoleId: role.RoleId, Status: "2"},
|
||||
{Username: usernames[1], NickName: "Synthetic other", RoleId: role.RoleId, Status: "2"},
|
||||
}
|
||||
if err := db.Create(&users).Error; err != nil {
|
||||
t.Fatalf("create synthetic administrators: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if err := db.Unscoped().Where("username IN ?", usernames).Delete(&models.SysUser{}).Error; err != nil {
|
||||
t.Errorf("delete synthetic administrators: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
userService := SysUser{Service: service.Service{Orm: db}}
|
||||
permission := &actions.DataPermission{}
|
||||
currentStatus := dto.UpdateSysUserStatusReq{UserId: users[0].UserId, Status: "1"}
|
||||
if err := userService.UpdateStatus(¤tStatus, permission, users[0].UserId); !errors.Is(err, ErrCurrentAdminProtected) {
|
||||
t.Fatalf("disable current administrator error = %v", err)
|
||||
}
|
||||
if err := userService.Remove(&dto.SysUserById{ObjectById: commonDto.ObjectById{Id: users[1].UserId}}, permission, users[0].UserId); err != nil {
|
||||
t.Fatalf("remove one of two active administrators: %v", err)
|
||||
}
|
||||
lastStatus := dto.UpdateSysUserStatusReq{UserId: users[0].UserId, Status: "1"}
|
||||
if err := userService.UpdateStatus(&lastStatus, permission, 999999); !errors.Is(err, ErrLastAdminProtected) {
|
||||
t.Fatalf("disable last administrator error = %v", err)
|
||||
}
|
||||
|
||||
roleService := SysRole{Service: service.Service{Orm: db}}
|
||||
if err := roleService.UpdateStatus(&dto.UpdateStatusReq{RoleId: role.RoleId, Status: "1"}); !errors.Is(err, ErrBuiltInRoleProtected) {
|
||||
t.Fatalf("disable built-in role error = %v", err)
|
||||
}
|
||||
if err := roleService.Update(&dto.SysRoleUpdateReq{RoleId: role.RoleId, RoleName: "Renamed", RoleKey: role.RoleKey, Status: "2"}, nil); !errors.Is(err, ErrBuiltInRoleProtected) {
|
||||
t.Fatalf("rename built-in role error = %v", err)
|
||||
}
|
||||
if err := roleService.Remove(&dto.SysRoleDeleteReq{Ids: []int{role.RoleId}}, nil); !errors.Is(err, ErrBuiltInRoleProtected) {
|
||||
t.Fatalf("delete built-in role error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func openAdminProtectionDB(t *testing.T) *gorm.DB {
|
||||
t.Helper()
|
||||
if os.Getenv("CHORUS_RUN_ADMIN_TESTS") != "1" {
|
||||
t.Skip("set CHORUS_RUN_ADMIN_TESTS=1 for the disposable MySQL database")
|
||||
}
|
||||
dsn := strings.TrimSpace(os.Getenv("CHORUS_DSN"))
|
||||
parsed, err := mysqldriver.ParseDSN(dsn)
|
||||
if err != nil {
|
||||
t.Fatalf("parse MySQL DSN: %v", err)
|
||||
}
|
||||
if parsed.DBName != "chorus_test" {
|
||||
t.Fatalf("refusing non-disposable database %q", parsed.DBName)
|
||||
}
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)})
|
||||
if err != nil {
|
||||
t.Fatalf("open disposable MySQL database: %v", err)
|
||||
}
|
||||
return db
|
||||
}
|
||||
Reference in New Issue
Block a user