85 lines
3.1 KiB
Go
85 lines
3.1 KiB
Go
package api
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
"vpn-platform/backend/internal/model"
|
|
"vpn-platform/backend/internal/store"
|
|
)
|
|
|
|
func TestClientSessionCannotExpandToSameNameOtherLine(t *testing.T) {
|
|
if err := store.Init(t.TempDir()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
hashA, _ := bcrypt.GenerateFromPassword([]byte("password-a"), bcrypt.MinCost)
|
|
hashB, _ := bcrypt.GenerateFromPassword([]byte("password-b"), bcrypt.MinCost)
|
|
lineA := model.Tunnel{Name: "A", Protocol: "wireguard"}
|
|
lineB := model.Tunnel{Name: "B", Protocol: "wireguard"}
|
|
store.DB.Create(&lineA)
|
|
store.DB.Create(&lineB)
|
|
uA := model.VPNUser{TunnelID: lineA.ID, Username: "same", Password: string(hashA), Enabled: true, Source: "local"}
|
|
uB := model.VPNUser{TunnelID: lineB.ID, Username: "same", Password: string(hashB), Enabled: true, Source: "local"}
|
|
store.DB.Create(&uA)
|
|
store.DB.Create(&uB)
|
|
passed, _, err := authorizedLines("same", "password-a", "")
|
|
if err != nil || len(passed) != 1 || passed[0].ID != uA.ID {
|
|
t.Fatalf("错误的授权记录: %+v, %v", passed, err)
|
|
}
|
|
token, _, err := issueClientToken("same", "laptop", "linux", passed)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var session model.ClientSession
|
|
if err := store.DB.Where("token_hash = ?", clientTokenHash(token)).First(&session).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
granted := currentGrantedUsers(&session)
|
|
if _, _, err := clientAuthorizedTunnel(granted, lineB.ID); err == nil {
|
|
t.Fatal("错误密码的同名线路不得获得授权")
|
|
}
|
|
if _, _, err := clientAuthorizedTunnel(granted, lineA.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
store.DB.Model(&uA).Update("password", string(hashB))
|
|
if got := currentGrantedUsers(&session); len(got) != 0 {
|
|
t.Fatalf("修改密码后旧授权必须失效: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestAllocateClientAddressSkipsUsedAndBoundaries(t *testing.T) {
|
|
used := map[string]bool{
|
|
"10.8.0.1": true, // 服务端自身
|
|
"10.8.0.2": true, // 已占用 Peer
|
|
}
|
|
addr, err := allocateClientAddress("10.8.0.1/24", used)
|
|
if err != nil {
|
|
t.Fatalf("allocate: %v", err)
|
|
}
|
|
if addr != "10.8.0.3/32" {
|
|
t.Fatalf("应分配第一个空闲地址 10.8.0.3/32,得到 %s", addr)
|
|
}
|
|
}
|
|
|
|
func TestAllocateClientAddressSmallSubnet(t *testing.T) {
|
|
// /30 只有 2 个可用主机位,均被占用时必须报错而不是返回网络/广播地址
|
|
used := map[string]bool{"10.8.0.1": true, "10.8.0.2": true}
|
|
if _, err := allocateClientAddress("10.8.0.1/30", used); err == nil {
|
|
t.Fatal("地址耗尽时必须报错")
|
|
}
|
|
if _, err := allocateClientAddress("10.8.0.1/31", map[string]bool{}); err == nil {
|
|
t.Fatal("/31 无可用主机位,必须报错")
|
|
}
|
|
if _, err := allocateClientAddress("fd00::1/64", map[string]bool{}); err == nil {
|
|
t.Fatal("IPv6 网段暂不支持自动分配,必须明确报错")
|
|
}
|
|
}
|
|
|
|
func TestAllocateClientAddressExhausted(t *testing.T) {
|
|
used := map[string]bool{"192.168.1.1": true, "192.168.1.2": true, "192.168.1.3": true,
|
|
"192.168.1.4": true, "192.168.1.5": true, "192.168.1.6": true}
|
|
if _, err := allocateClientAddress("192.168.1.1/29", used); err == nil {
|
|
t.Fatal("/29 全部占用时必须报错")
|
|
}
|
|
}
|