- config: 为 Config 和所有子配置结构添加完整文档,包含使用示例和注意事项 - stream: 为负载均衡器和服务器添加详细的参数、返回值和功能说明 - logging: 为日志格式化和输出函数添加文档,说明支持的变量替换 - handler: 为路由器、静态文件和 sendfile 处理器添加文档 - proxy: 为健康检查器和代理功能添加完整文档 - cache/server/ssl/middleware: 补充相关模块的文档注释 - config.example.yaml: 添加可信代理配置、加密套件示例,更新压缩级别说明 Co-Authored-By: Claude <noreply@anthropic.com>
354 lines
7.0 KiB
Go
354 lines
7.0 KiB
Go
// Package security 提供访问控制功能的测试。
|
||
//
|
||
// 该文件测试访问控制模块的各项功能,包括:
|
||
// - 访问控制器创建
|
||
// - IP 地址检查
|
||
// - 允许列表和拒绝列表
|
||
// - CIDR 解析
|
||
// - 列表更新和统计
|
||
//
|
||
// 作者:xfy
|
||
package security
|
||
|
||
import (
|
||
"net"
|
||
"testing"
|
||
|
||
"github.com/valyala/fasthttp"
|
||
"rua.plus/lolly/internal/config"
|
||
)
|
||
|
||
func TestNewAccessControl(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
cfg *config.AccessConfig
|
||
wantErr bool
|
||
}{
|
||
{
|
||
name: "nil config",
|
||
cfg: nil,
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "empty config",
|
||
cfg: &config.AccessConfig{},
|
||
},
|
||
{
|
||
name: "valid allow list",
|
||
cfg: &config.AccessConfig{
|
||
Allow: []string{"192.168.1.0/24", "10.0.0.1"},
|
||
},
|
||
},
|
||
{
|
||
name: "valid deny list",
|
||
cfg: &config.AccessConfig{
|
||
Deny: []string{"192.168.2.100/32"},
|
||
},
|
||
},
|
||
{
|
||
name: "invalid CIDR",
|
||
cfg: &config.AccessConfig{
|
||
Allow: []string{"invalid"},
|
||
},
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "default allow",
|
||
cfg: &config.AccessConfig{
|
||
Default: "allow",
|
||
},
|
||
},
|
||
{
|
||
name: "default deny",
|
||
cfg: &config.AccessConfig{
|
||
Default: "deny",
|
||
},
|
||
},
|
||
{
|
||
name: "invalid default",
|
||
cfg: &config.AccessConfig{
|
||
Default: "invalid",
|
||
},
|
||
wantErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
ac, err := NewAccessControl(tt.cfg)
|
||
if (err != nil) != tt.wantErr {
|
||
t.Errorf("NewAccessControl() error = %v, wantErr %v", err, tt.wantErr)
|
||
}
|
||
if !tt.wantErr && ac == nil {
|
||
t.Error("Expected non-nil AccessControl")
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestAccessControlCheck(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
cfg *config.AccessConfig
|
||
ip string
|
||
expected bool
|
||
}{
|
||
{
|
||
name: "default allow",
|
||
cfg: &config.AccessConfig{
|
||
Default: "allow",
|
||
},
|
||
ip: "192.168.1.100",
|
||
expected: true,
|
||
},
|
||
{
|
||
name: "default deny",
|
||
cfg: &config.AccessConfig{
|
||
Default: "deny",
|
||
},
|
||
ip: "192.168.1.100",
|
||
expected: false,
|
||
},
|
||
{
|
||
name: "explicit allow",
|
||
cfg: &config.AccessConfig{
|
||
Allow: []string{"192.168.1.0/24"},
|
||
Default: "deny",
|
||
},
|
||
ip: "192.168.1.100",
|
||
expected: true,
|
||
},
|
||
{
|
||
name: "not in allow list",
|
||
cfg: &config.AccessConfig{
|
||
Allow: []string{"192.168.1.0/24"},
|
||
Default: "deny",
|
||
},
|
||
ip: "192.168.2.100",
|
||
expected: false,
|
||
},
|
||
{
|
||
name: "explicit deny",
|
||
cfg: &config.AccessConfig{
|
||
Deny: []string{"192.168.2.100"},
|
||
Default: "allow",
|
||
},
|
||
ip: "192.168.2.100",
|
||
expected: false,
|
||
},
|
||
{
|
||
name: "deny takes precedence",
|
||
cfg: &config.AccessConfig{
|
||
Allow: []string{"192.168.0.0/16"},
|
||
Deny: []string{"192.168.2.100"},
|
||
Default: "deny",
|
||
},
|
||
ip: "192.168.2.100",
|
||
expected: false,
|
||
},
|
||
{
|
||
name: "single IP allow",
|
||
cfg: &config.AccessConfig{
|
||
Allow: []string{"10.0.0.1"},
|
||
Default: "deny",
|
||
},
|
||
ip: "10.0.0.1",
|
||
expected: true,
|
||
},
|
||
{
|
||
name: "IPv6 allow",
|
||
cfg: &config.AccessConfig{
|
||
Allow: []string{"2001:db8::/32"},
|
||
Default: "deny",
|
||
},
|
||
ip: "2001:db8::1",
|
||
expected: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
ac, err := NewAccessControl(tt.cfg)
|
||
if err != nil {
|
||
t.Fatalf("NewAccessControl() error: %v", err)
|
||
}
|
||
|
||
ip := net.ParseIP(tt.ip)
|
||
if ip == nil {
|
||
t.Fatalf("Invalid IP: %s", tt.ip)
|
||
}
|
||
|
||
result := ac.Check(ip)
|
||
if result != tt.expected {
|
||
t.Errorf("Check(%s) = %v, expected %v", tt.ip, result, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestAccessControlProcess(t *testing.T) {
|
||
ac, err := NewAccessControl(&config.AccessConfig{
|
||
Allow: []string{"127.0.0.1"},
|
||
Default: "deny",
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("NewAccessControl() error: %v", err)
|
||
}
|
||
|
||
// Create a simple handler
|
||
nextHandler := func(ctx *fasthttp.RequestCtx) {
|
||
_, _ = ctx.WriteString("OK")
|
||
}
|
||
|
||
handler := ac.Process(nextHandler)
|
||
|
||
// Verify the handler is created correctly
|
||
if handler == nil {
|
||
t.Error("Process() returned nil handler")
|
||
}
|
||
}
|
||
|
||
func TestParseCIDR(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
cidr string
|
||
wantErr bool
|
||
}{
|
||
{
|
||
name: "valid IPv4 CIDR",
|
||
cidr: "192.168.1.0/24",
|
||
},
|
||
{
|
||
name: "valid IPv4 single",
|
||
cidr: "192.168.1.1",
|
||
},
|
||
{
|
||
name: "valid IPv6 CIDR",
|
||
cidr: "2001:db8::/32",
|
||
},
|
||
{
|
||
name: "valid IPv6 single",
|
||
cidr: "2001:db8::1",
|
||
},
|
||
{
|
||
name: "invalid IP",
|
||
cidr: "invalid",
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "invalid CIDR",
|
||
cidr: "192.168.1.0/33",
|
||
wantErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
network, err := parseCIDR(tt.cidr)
|
||
if (err != nil) != tt.wantErr {
|
||
t.Errorf("parseCIDR() error = %v, wantErr %v", err, tt.wantErr)
|
||
}
|
||
if !tt.wantErr && network == nil {
|
||
t.Error("Expected non-nil network")
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestUpdateAllowList(t *testing.T) {
|
||
ac, err := NewAccessControl(&config.AccessConfig{
|
||
Default: "deny",
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("NewAccessControl() error: %v", err)
|
||
}
|
||
|
||
// Update allow list
|
||
err = ac.UpdateAllowList([]string{"10.0.0.0/8"})
|
||
if err != nil {
|
||
t.Errorf("UpdateAllowList() error: %v", err)
|
||
}
|
||
|
||
// Check that IP is now allowed
|
||
ip := net.ParseIP("10.0.0.1")
|
||
if !ac.Check(ip) {
|
||
t.Error("Expected IP to be allowed after update")
|
||
}
|
||
|
||
// Test invalid update
|
||
err = ac.UpdateAllowList([]string{"invalid"})
|
||
if err == nil {
|
||
t.Error("Expected error for invalid CIDR")
|
||
}
|
||
}
|
||
|
||
func TestUpdateDenyList(t *testing.T) {
|
||
ac, err := NewAccessControl(&config.AccessConfig{
|
||
Allow: []string{"0.0.0.0/0"},
|
||
Default: "allow",
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("NewAccessControl() error: %v", err)
|
||
}
|
||
|
||
// Update deny list
|
||
err = ac.UpdateDenyList([]string{"192.168.2.0/24"})
|
||
if err != nil {
|
||
t.Errorf("UpdateDenyList() error: %v", err)
|
||
}
|
||
|
||
// Check that IP is now denied
|
||
ip := net.ParseIP("192.168.2.1")
|
||
if ac.Check(ip) {
|
||
t.Error("Expected IP to be denied after update")
|
||
}
|
||
}
|
||
|
||
func TestSetDefault(t *testing.T) {
|
||
ac, err := NewAccessControl(&config.AccessConfig{
|
||
Default: "allow",
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("NewAccessControl() error: %v", err)
|
||
}
|
||
|
||
// Change to deny
|
||
err = ac.SetDefault("deny")
|
||
if err != nil {
|
||
t.Errorf("SetDefault() error: %v", err)
|
||
}
|
||
|
||
stats := ac.GetStats()
|
||
if stats.Default != "deny" {
|
||
t.Errorf("Expected default 'deny', got %s", stats.Default)
|
||
}
|
||
|
||
// Test invalid action
|
||
err = ac.SetDefault("invalid")
|
||
if err == nil {
|
||
t.Error("Expected error for invalid action")
|
||
}
|
||
}
|
||
|
||
func TestGetStats(t *testing.T) {
|
||
ac, err := NewAccessControl(&config.AccessConfig{
|
||
Allow: []string{"192.168.1.0/24", "10.0.0.0/8"},
|
||
Deny: []string{"192.168.2.100"},
|
||
Default: "deny",
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("NewAccessControl() error: %v", err)
|
||
}
|
||
|
||
stats := ac.GetStats()
|
||
if stats.AllowCount != 2 {
|
||
t.Errorf("Expected AllowCount 2, got %d", stats.AllowCount)
|
||
}
|
||
if stats.DenyCount != 1 {
|
||
t.Errorf("Expected DenyCount 1, got %d", stats.DenyCount)
|
||
}
|
||
if stats.Default != "deny" {
|
||
t.Errorf("Expected Default 'deny', got %s", stats.Default)
|
||
}
|
||
}
|