!45 feat: per-entry enable switches for IP/URL black and white lists

Merge pull request !45 from samwaf/feat_list_switches
This commit is contained in:
samwaf
2026-10-21 08:22:03 +00:00
committed by Gitee
25 changed files with 555 additions and 19 deletions
+38 -1
View File
@@ -37,6 +37,10 @@ func (w *WafAllowIpApi) AddApi(c *gin.Context) {
return
}
req.IpType, req.Ip, req.GroupCode = ipType, ip, groupCode
if req.IsEnable != nil && *req.IsEnable != 0 && *req.IsEnable != 1 {
response.FailWithMessage("is_enable 取值只能是 0 或 1", c)
return
}
err = wafIpAllowService.CheckIsExistApi(req)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
@@ -156,6 +160,10 @@ func (w *WafAllowIpApi) ModifyAllowIpApi(c *gin.Context) {
return
}
req.IpType, req.Ip, req.GroupCode = ipType, ip, groupCode
if req.IsEnable != nil && *req.IsEnable != 0 && *req.IsEnable != 1 {
response.FailWithMessage("is_enable 取值只能是 0 或 1", c)
return
}
//编辑前先取旧记录,拿到可能被本次编辑改掉的旧 host_code(issue #898)
bean := wafIpAllowService.GetDetailByIdApi(req.Id)
@@ -172,13 +180,42 @@ func (w *WafAllowIpApi) ModifyAllowIpApi(c *gin.Context) {
}
}
// ToggleApi 切换IP白名单条目启用状态
// @Summary 切换IP白名单条目启用状态
// @Description 启用或停用单条IP白名单记录,停用的条目不再对引擎生效
// @Tags 网站防护-IP白名单
// @Accept json
// @Produce json
// @Param data body request.WafAllowIpToggleReq true "条目ID与目标状态"
// @Success 200 {object} response.Response "操作成功"
// @Security ApiKeyAuth
// @Router /wafhost/ipwhite/toggle [post]
func (w *WafAllowIpApi) ToggleApi(c *gin.Context) {
var req request.WafAllowIpToggleReq
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage("解析失败", c)
return
}
if *req.IsEnable != 0 && *req.IsEnable != 1 {
response.FailWithMessage("is_enable 取值只能是 0 或 1", c)
return
}
hostCode, err := wafIpAllowService.ToggleApi(req)
if err != nil {
response.FailWithMessage("操作失败", c)
return
}
w.NotifyWaf(hostCode)
response.OkWithMessage("操作成功", c)
}
/*
*
通知到waf引擎实时生效
*/
func (w *WafAllowIpApi) NotifyWaf(host_code string) {
var ipWhites []model.IPAllowList
global.GWAF_LOCAL_DB.Where("host_code = ? ", host_code).Find(&ipWhites)
global.GWAF_LOCAL_DB.Where("host_code = ? and is_enable = 1", host_code).Find(&ipWhites)
var chanInfo = spec.ChanCommonHost{
HostCode: host_code,
Type: enums.ChanTypeAllowIP,
+38 -1
View File
@@ -30,6 +30,10 @@ func (w *WafAllowUrlApi) AddApi(c *gin.Context) {
var req request.WafAllowUrlAddReq
err := c.ShouldBindJSON(&req)
if err == nil {
if req.IsEnable != nil && *req.IsEnable != 0 && *req.IsEnable != 1 {
response.FailWithMessage("is_enable 取值只能是 0 或 1", c)
return
}
err = wafUrlAllowService.CheckIsExistApi(req)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
err = wafUrlAllowService.AddApi(req)
@@ -142,6 +146,10 @@ func (w *WafAllowUrlApi) ModifyAllowUrlApi(c *gin.Context) {
var req request.WafAllowUrlEditReq
err := c.ShouldBindJSON(&req)
if err == nil {
if req.IsEnable != nil && *req.IsEnable != 0 && *req.IsEnable != 1 {
response.FailWithMessage("is_enable 取值只能是 0 或 1", c)
return
}
//编辑前先取旧记录,拿到可能被本次编辑改掉的旧 host_code(issue #898)
bean := wafUrlAllowService.GetDetailByIdApi(req.Id)
err = wafUrlAllowService.ModifyApi(req)
@@ -157,13 +165,42 @@ func (w *WafAllowUrlApi) ModifyAllowUrlApi(c *gin.Context) {
}
}
// ToggleApi 切换URL白名单条目启用状态
// @Summary 切换URL白名单条目启用状态
// @Description 启用或停用单条URL白名单记录,停用的条目不再对引擎生效
// @Tags 网站防护-URL白名单
// @Accept json
// @Produce json
// @Param data body request.WafAllowUrlToggleReq true "条目ID与目标状态"
// @Success 200 {object} response.Response "操作成功"
// @Security ApiKeyAuth
// @Router /wafhost/urlwhite/toggle [post]
func (w *WafAllowUrlApi) ToggleApi(c *gin.Context) {
var req request.WafAllowUrlToggleReq
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage("解析失败", c)
return
}
if *req.IsEnable != 0 && *req.IsEnable != 1 {
response.FailWithMessage("is_enable 取值只能是 0 或 1", c)
return
}
hostCode, err := wafUrlAllowService.ToggleApi(req)
if err != nil {
response.FailWithMessage("操作失败", c)
return
}
w.NotifyWaf(hostCode)
response.OkWithMessage("操作成功", c)
}
/*
*
通知到waf引擎实时生效
*/
func (w *WafAllowUrlApi) NotifyWaf(host_code string) {
var urlWhites []model.URLAllowList
global.GWAF_LOCAL_DB.Where("host_code = ? ", host_code).Find(&urlWhites)
global.GWAF_LOCAL_DB.Where("host_code = ? and is_enable = 1", host_code).Find(&urlWhites)
var chanInfo = spec.ChanCommonHost{
HostCode: host_code,
Type: enums.ChanTypeAllowURL,
+38 -1
View File
@@ -41,6 +41,10 @@ func (w *WafBlockIpApi) AddApi(c *gin.Context) {
return
}
req.IpType, req.Ip, req.GroupCode = ipType, ip, groupCode
if req.IsEnable != nil && *req.IsEnable != 0 && *req.IsEnable != 1 {
response.FailWithMessage("is_enable 取值只能是 0 或 1", c)
return
}
// 封禁层级:""/waf=WAF应用层(默认) | system=系统防火墙 | both=两者
layer := req.TargetLayer
@@ -196,6 +200,10 @@ func (w *WafBlockIpApi) ModifyBlockIpApi(c *gin.Context) {
return
}
req.IpType, req.Ip, req.GroupCode = ipType, ip, groupCode
if req.IsEnable != nil && *req.IsEnable != 0 && *req.IsEnable != 1 {
response.FailWithMessage("is_enable 取值只能是 0 或 1", c)
return
}
//编辑前先取旧记录,拿到可能被本次编辑改掉的旧 host_code(issue #898)
bean := wafIpBlockService.GetDetailByIdApi(req.Id)
@@ -271,13 +279,42 @@ func (w *WafBlockIpApi) DelAllBlockIpApi(c *gin.Context) {
}
}
// ToggleApi 切换IP黑名单条目启用状态
// @Summary 切换IP黑名单条目启用状态
// @Description 启用或停用单条IP黑名单记录,停用的条目不再对引擎生效
// @Tags 网站防护-IP黑名单
// @Accept json
// @Produce json
// @Param data body request.WafBlockIpToggleReq true "条目ID与目标状态"
// @Success 200 {object} response.Response "操作成功"
// @Security ApiKeyAuth
// @Router /wafhost/ipblock/toggle [post]
func (w *WafBlockIpApi) ToggleApi(c *gin.Context) {
var req request.WafBlockIpToggleReq
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage("解析失败", c)
return
}
if *req.IsEnable != 0 && *req.IsEnable != 1 {
response.FailWithMessage("is_enable 取值只能是 0 或 1", c)
return
}
hostCode, err := wafIpBlockService.ToggleApi(req)
if err != nil {
response.FailWithMessage("操作失败", c)
return
}
w.NotifyWaf(hostCode)
response.OkWithMessage("操作成功", c)
}
/*
*
通知到waf引擎实时生效
*/
func (w *WafBlockIpApi) NotifyWaf(host_code string) {
var ipWhites []model.IPBlockList
global.GWAF_LOCAL_DB.Where("host_code = ? ", host_code).Find(&ipWhites)
global.GWAF_LOCAL_DB.Where("host_code = ? and is_enable = 1", host_code).Find(&ipWhites)
var chanInfo = spec.ChanCommonHost{
HostCode: host_code,
Type: enums.ChanTypeBlockIP,
+38 -1
View File
@@ -30,6 +30,10 @@ func (w *WafBlockUrlApi) AddApi(c *gin.Context) {
var req request.WafBlockUrlAddReq
err := c.ShouldBindJSON(&req)
if err == nil {
if req.IsEnable != nil && *req.IsEnable != 0 && *req.IsEnable != 1 {
response.FailWithMessage("is_enable 取值只能是 0 或 1", c)
return
}
err = wafUrlBlockService.CheckIsExistApi(req)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
err = wafUrlBlockService.AddApi(req)
@@ -142,6 +146,10 @@ func (w *WafBlockUrlApi) ModifyBlockUrlApi(c *gin.Context) {
var req request.WafBlockUrlEditReq
err := c.ShouldBindJSON(&req)
if err == nil {
if req.IsEnable != nil && *req.IsEnable != 0 && *req.IsEnable != 1 {
response.FailWithMessage("is_enable 取值只能是 0 或 1", c)
return
}
//编辑前先取旧记录,拿到可能被本次编辑改掉的旧 host_code(issue #898)
bean := wafUrlBlockService.GetDetailByIdApi(req.Id)
err = wafUrlBlockService.ModifyApi(req)
@@ -157,13 +165,42 @@ func (w *WafBlockUrlApi) ModifyBlockUrlApi(c *gin.Context) {
}
}
// ToggleApi 切换URL黑名单条目启用状态
// @Summary 切换URL黑名单条目启用状态
// @Description 启用或停用单条URL黑名单记录,停用的条目不再对引擎生效
// @Tags 网站防护-URL黑名单
// @Accept json
// @Produce json
// @Param data body request.WafBlockUrlToggleReq true "条目ID与目标状态"
// @Success 200 {object} response.Response "操作成功"
// @Security ApiKeyAuth
// @Router /wafhost/urlblock/toggle [post]
func (w *WafBlockUrlApi) ToggleApi(c *gin.Context) {
var req request.WafBlockUrlToggleReq
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage("解析失败", c)
return
}
if *req.IsEnable != 0 && *req.IsEnable != 1 {
response.FailWithMessage("is_enable 取值只能是 0 或 1", c)
return
}
hostCode, err := wafUrlBlockService.ToggleApi(req)
if err != nil {
response.FailWithMessage("操作失败", c)
return
}
w.NotifyWaf(hostCode)
response.OkWithMessage("操作成功", c)
}
/*
*
通知到waf引擎实时生效
*/
func (w *WafBlockUrlApi) NotifyWaf(host_code string) {
var urlWhites []model.URLBlockList
global.GWAF_LOCAL_DB.Where("host_code = ? ", host_code).Find(&urlWhites)
global.GWAF_LOCAL_DB.Where("host_code = ? and is_enable = 1", host_code).Find(&urlWhites)
var chanInfo = spec.ChanCommonHost{
HostCode: host_code,
Type: enums.ChanTypeBlockURL,
+2
View File
@@ -13,6 +13,7 @@ type IPAllowList struct {
// 存量行为空串,等同于 ip,判定处一律只判 == IPEntryTypeGroup。
IpType string `gorm:"size:20" json:"ip_type"`
GroupCode string `gorm:"size:64" json:"group_code"` //IpType=group 时指向 ip_group.group_code
IsEnable int `gorm:"column:is_enable" json:"is_enable"` //1启用 0停用(停用条目不下发引擎)
}
type URLAllowList struct {
baseorm.BaseOrm
@@ -20,4 +21,5 @@ type URLAllowList struct {
CompareType string `gorm:"size:50" json:"compare_type"` //判断类型,包含、开始、结束、完全匹配
Url string `gorm:"type:text" json:"url"` //请求地址
Remarks string `gorm:"size:500" json:"remarks"` //备注
IsEnable int `gorm:"column:is_enable" json:"is_enable"` //1启用 0停用(停用条目不下发引擎)
}
+2
View File
@@ -13,6 +13,7 @@ type IPBlockList struct {
// 存量行为空串,等同于 ip,判定处一律只判 == IPEntryTypeGroup。
IpType string `gorm:"size:20" json:"ip_type"`
GroupCode string `gorm:"size:64" json:"group_code"` //IpType=group 时指向 ip_group.group_code
IsEnable int `gorm:"column:is_enable" json:"is_enable"` //1启用 0停用(停用条目不下发引擎)
}
type URLBlockList struct {
@@ -21,4 +22,5 @@ type URLBlockList struct {
CompareType string `gorm:"size:50" json:"compare_type" form:"compare_type"` //对比方式
Url string `gorm:"type:text" json:"url"` //限制请求地址
Remarks string `gorm:"size:500" json:"remarks"` //备注
IsEnable int `gorm:"column:is_enable" json:"is_enable"` //1启用 0停用(停用条目不下发引擎)
}
+7
View File
@@ -10,6 +10,7 @@ type WafAllowIpAddReq struct {
Remarks string `json:"remarks"` //备注
IpType string `json:"ip_type"` //条目类型: ""/ip(单条) | group(引用IP组)
GroupCode string `json:"group_code"` //IpType=group 时的IP组短码
IsEnable *int `json:"is_enable"` //启用状态:nil=新增默认启用/编辑不改动,0停用 1启用
}
type WafAllowIpDelReq struct {
Id string `json:"id" form:"id"` //白名单IP唯一键
@@ -25,6 +26,7 @@ type WafAllowIpEditReq struct {
Remarks string `json:"remarks"` //备注
IpType string `json:"ip_type"` //条目类型: ""/ip(单条) | group(引用IP组)
GroupCode string `json:"group_code"` //IpType=group 时的IP组短码
IsEnable *int `json:"is_enable"` //启用状态:nil=新增默认启用/编辑不改动,0停用 1启用
}
type WafAllowIpSearchReq struct {
HostCode string `json:"host_code" ` //主机码
@@ -39,3 +41,8 @@ type WafAllowIpBatchDelReq struct {
type WafAllowIpDelAllReq struct {
HostCode string `json:"host_code" form:"host_code"` //网站唯一码,为空则删除所有
}
type WafAllowIpToggleReq struct {
Id string `json:"id" form:"id" binding:"required"` //条目唯一键
IsEnable *int `json:"is_enable" form:"is_enable" binding:"required"` //1启用 0停用(必填,防漏字段被当成停用)
}
+7
View File
@@ -7,6 +7,7 @@ type WafAllowUrlAddReq struct {
CompareType string `json:"compare_type" form:"compare_type" binding:"required"` //对比方式
Url string `json:"url" binding:"required"` //白名单url
Remarks string `json:"remarks"` //备注
IsEnable *int `json:"is_enable"` //启用状态:nil=新增默认启用/编辑不改动,0停用 1启用
}
type WafAllowUrlDelReq struct {
Id string `json:"id" form:"id"` //白名单url唯一键
@@ -21,6 +22,7 @@ type WafAllowUrlEditReq struct {
CompareType string `json:"compare_type" form:"compare_type" binding:"required"` //对比方式
Url string `json:"url" binding:"required"` //白名单url
Remarks string `json:"remarks"` //备注
IsEnable *int `json:"is_enable"` //启用状态:nil=新增默认启用/编辑不改动,0停用 1启用
}
type WafAllowUrlSearchReq struct {
@@ -36,3 +38,8 @@ type WafAllowUrlBatchDelReq struct {
type WafAllowUrlDelAllReq struct {
HostCode string `json:"host_code" form:"host_code"` //网站唯一码
}
type WafAllowUrlToggleReq struct {
Id string `json:"id" form:"id" binding:"required"` //条目唯一键
IsEnable *int `json:"is_enable" form:"is_enable" binding:"required"` //1启用 0停用(必填,防漏字段被当成停用)
}
+7
View File
@@ -12,6 +12,7 @@ type WafBlockIpAddReq struct {
TargetLayer string `json:"target_layer"` //封禁层级: ""/waf(WAF应用层) | system(系统防火墙) | both(两者)
IpType string `json:"ip_type"` //条目类型: ""/ip(单条) | group(引用IP组)
GroupCode string `json:"group_code"` //IpType=group 时的IP组短码
IsEnable *int `json:"is_enable"` //启用状态:nil=新增默认启用/编辑不改动,0停用 1启用
}
type WafBlockIpEditReq struct {
@@ -21,6 +22,7 @@ type WafBlockIpEditReq struct {
Remarks string `json:"remarks"` //备注
IpType string `json:"ip_type"` //条目类型: ""/ip(单条) | group(引用IP组)
GroupCode string `json:"group_code"` //IpType=group 时的IP组短码
IsEnable *int `json:"is_enable"` //启用状态:nil=新增默认启用/编辑不改动,0停用 1启用
}
type WafBlockIpDelReq struct {
Id string `json:"id" form:"id"` //Block IP唯一键
@@ -44,3 +46,8 @@ type WafBlockIpBatchDelReq struct {
type WafBlockIpDelAllReq struct {
HostCode string `json:"host_code" form:"host_code"` //网站唯一码,为空则删除所有
}
type WafBlockIpToggleReq struct {
Id string `json:"id" form:"id" binding:"required"` //条目唯一键
IsEnable *int `json:"is_enable" form:"is_enable" binding:"required"` //1启用 0停用(必填,防漏字段被当成停用)
}
+7
View File
@@ -7,6 +7,7 @@ type WafBlockUrlAddReq struct {
CompareType string `json:"compare_type" form:"compare_type" binding:"required"` //对比方式
Url string `json:"url" binding:"required"` //Block url
Remarks string `json:"remarks"` //备注
IsEnable *int `json:"is_enable"` //启用状态:nil=新增默认启用/编辑不改动,0停用 1启用
}
type WafBlockUrlSearchReq struct {
HostCode string `json:"host_code" ` //主机码
@@ -26,6 +27,7 @@ type WafBlockUrlEditReq struct {
CompareType string `json:"compare_type" form:"compare_type" binding:"required"` //对比方式
Url string `json:"url" binding:"required"` //Block url
Remarks string `json:"remarks"` //备注
IsEnable *int `json:"is_enable"` //启用状态:nil=新增默认启用/编辑不改动,0停用 1启用
}
// 批量删除请求结构体
@@ -37,3 +39,8 @@ type WafBlockUrlBatchDelReq struct {
type WafBlockUrlDelAllReq struct {
HostCode string `json:"host_code"` // 网站唯一码,为空则删除所有
}
type WafBlockUrlToggleReq struct {
Id string `json:"id" form:"id" binding:"required"` //条目唯一键
IsEnable *int `json:"is_enable" form:"is_enable" binding:"required"` //1启用 0停用(必填,防漏字段被当成停用)
}
+1
View File
@@ -16,6 +16,7 @@ func (receiver *AllowIpRouter) InitAllowIpRouter(group *gin.RouterGroup) {
allowIpRouter.POST("/api/v1/wafhost/ipwhite/add", AllowIpRouterApi.AddApi)
allowIpRouter.GET("/api/v1/wafhost/ipwhite/del", AllowIpRouterApi.DelAllowIpApi)
allowIpRouter.POST("/api/v1/wafhost/ipwhite/edit", AllowIpRouterApi.ModifyAllowIpApi)
allowIpRouter.POST("/api/v1/wafhost/ipwhite/toggle", AllowIpRouterApi.ToggleApi)
allowIpRouter.POST("/api/v1/wafhost/ipwhite/batchdel", AllowIpRouterApi.BatchDelAllowIpApi)
allowIpRouter.POST("/api/v1/wafhost/ipwhite/delall", AllowIpRouterApi.DelAllAllowIpApi)
+1
View File
@@ -16,6 +16,7 @@ func (receiver *AllowUrlRouter) InitAllowUrlRouter(group *gin.RouterGroup) {
allowUrlRouter.POST("/api/v1/wafhost/urlwhite/add", AllowUrlRouterApi.AddApi)
allowUrlRouter.GET("/api/v1/wafhost/urlwhite/del", AllowUrlRouterApi.DelAllowUrlApi)
allowUrlRouter.POST("/api/v1/wafhost/urlwhite/edit", AllowUrlRouterApi.ModifyAllowUrlApi)
allowUrlRouter.POST("/api/v1/wafhost/urlwhite/toggle", AllowUrlRouterApi.ToggleApi)
// 新增批量删除路由
allowUrlRouter.POST("/api/v1/wafhost/urlwhite/batchdel", AllowUrlRouterApi.BatchDelAllowUrlApi)
// 新增全部删除路由
+1
View File
@@ -17,6 +17,7 @@ func (receiver *BlockIpRouter) InitBlockIpRouter(group *gin.RouterGroup) {
router.GET("/api/v1/wafhost/ipblock/recommend-layer", api.GetRecommendLayerApi) // 推荐封禁层级(智能默认)
router.GET("/api/v1/wafhost/ipblock/del", api.DelBlockIpApi)
router.POST("/api/v1/wafhost/ipblock/edit", api.ModifyBlockIpApi)
router.POST("/api/v1/wafhost/ipblock/toggle", api.ToggleApi)
router.POST("/api/v1/wafhost/ipblock/batch/del", api.BatchDelBlockIpApi)
router.POST("/api/v1/wafhost/ipblock/delall", api.DelAllBlockIpApi)
}
+1
View File
@@ -16,6 +16,7 @@ func (receiver *BlockUrlRouter) InitBlockUrlRouter(group *gin.RouterGroup) {
router.POST("/api/v1/wafhost/urlblock/add", api.AddApi)
router.GET("/api/v1/wafhost/urlblock/del", api.DelBlockUrlApi)
router.POST("/api/v1/wafhost/urlblock/edit", api.ModifyBlockUrlApi)
router.POST("/api/v1/wafhost/urlblock/toggle", api.ToggleApi)
router.POST("/api/v1/wafhost/urlblock/batchdel", api.BatchDelBlockUrlApi)
router.POST("/api/v1/wafhost/urlblock/delall", api.DelAllBlockUrlApi)
}
+188 -2
View File
@@ -32,6 +32,8 @@ func runCoreCases(t *testing.T, db *gorm.DB) {
// ————— 新增:各 service 完整 CRUD 回路 —————
runCoreCRUDCases(t, db)
runCoreCRUDCasesB(t, db)
// 名单条目启用开关(is_enable)三库回归
runListEnableSwitchCases(t, db)
// ————— 升级须知:生成幂等 / 状态流转 / 降级不生成 —————
runUpgradeNoticeCases(t, db)
}
@@ -324,12 +326,12 @@ func runCoreCRUDCases(t *testing.T, db *gorm.DB) {
t.Run("BatchTask", func(t *testing.T) {
name := "bt_" + sfx()
fatalIf(t, WafBatchServiceApp.AddApi(request.BatchTaskAddReq{
BatchTaskName: name, BatchType: "ip_black", BatchSourceType: "local", BatchTriggerType: "manual", BatchSource: "/tmp/x", BatchExecuteMethod: "append",
BatchTaskName: name, BatchType: "ipdeny", BatchSourceType: "local", BatchTriggerType: "manual", BatchSource: "/tmp/x", BatchExecuteMethod: "append",
}))
var bean model.BatchTask
firstBy(t, db, &bean, "batch_task_name = ?", name)
fatalIf(t, WafBatchServiceApp.ModifyApi(request.BatchTaskEditReq{
Id: bean.Id, BatchTaskName: name, BatchType: "ip_black", BatchSourceType: "local", BatchTriggerType: "manual", BatchSource: "/tmp/y", BatchExecuteMethod: "append",
Id: bean.Id, BatchTaskName: name, BatchType: "ipdeny", BatchSourceType: "local", BatchTriggerType: "manual", BatchSource: "/tmp/y", BatchExecuteMethod: "append",
}))
got := WafBatchServiceApp.GetDetailByIdApi(bean.Id)
if got.BatchSource != "/tmp/y" {
@@ -377,3 +379,187 @@ func runCoreCRUDCases(t *testing.T, db *gorm.DB) {
assertGone(t, db, &model.DelayMsg{}, bean.Id)
})
}
// ========================= 名单条目启用开关(is_enable) =========================
// runListEnableSwitchCases 四类名单 is_enable 三库一致性:
// - service 新增未传开关字段 → 启用(产品默认)
// - service 新增显式 0 → 停用(弹窗关着开关新增;防 default 标签把 0 吞成 1 的回归)
// - toggle 0→读回 0→toggle 1→读回 1
// - 裸结构体 Create 不带该字段 → 0(字段无 default 标签的语义锚;
// 若有人往模型上加回 default 标签,此用例会先红)
func runListEnableSwitchCases(t *testing.T, db *gorm.DB) {
type iostub struct {
addNil func() error
addZero func() error
addOne func() error
toggle func(id string, v int) error
findByHC func(hostCode string) (int, error)
}
zero, one := 0, 1
mk := []struct {
name string
kit func() iostub
}{
{"LSV_AllowIP", func() iostub {
return iostub{
addNil: func() error {
return WafWhiteIpServiceApp.AddApi(request.WafAllowIpAddReq{HostCode: "hc_nil", Ip: "10.10.0.1", Remarks: "x"})
},
addZero: func() error {
return WafWhiteIpServiceApp.AddApi(request.WafAllowIpAddReq{HostCode: "hc_zero", Ip: "10.10.0.2", Remarks: "x", IsEnable: &zero})
},
addOne: func() error {
return WafWhiteIpServiceApp.AddApi(request.WafAllowIpAddReq{HostCode: "hc_one", Ip: "10.10.0.3", Remarks: "x", IsEnable: &one})
},
toggle: func(id string, v int) error {
val := v
_, err := WafWhiteIpServiceApp.ToggleApi(request.WafAllowIpToggleReq{Id: id, IsEnable: &val})
return err
},
findByHC: func(hostCode string) (int, error) {
var b model.IPAllowList
if err := db.Where("host_code = ?", hostCode).First(&b).Error; err != nil {
return -1, err
}
return b.IsEnable, nil
},
}
}},
{"LSV_BlockIP", func() iostub {
return iostub{
addNil: func() error {
return WafBlockIpServiceApp.AddApi(request.WafBlockIpAddReq{HostCode: "hc_nil", Ip: "10.10.1.1", Remarks: "x"})
},
addZero: func() error {
return WafBlockIpServiceApp.AddApi(request.WafBlockIpAddReq{HostCode: "hc_zero", Ip: "10.10.1.2", Remarks: "x", IsEnable: &zero})
},
addOne: func() error {
return WafBlockIpServiceApp.AddApi(request.WafBlockIpAddReq{HostCode: "hc_one", Ip: "10.10.1.3", Remarks: "x", IsEnable: &one})
},
toggle: func(id string, v int) error {
val := v
_, err := WafBlockIpServiceApp.ToggleApi(request.WafBlockIpToggleReq{Id: id, IsEnable: &val})
return err
},
findByHC: func(hostCode string) (int, error) {
var b model.IPBlockList
if err := db.Where("host_code = ?", hostCode).First(&b).Error; err != nil {
return -1, err
}
return b.IsEnable, nil
},
}
}},
{"LSV_AllowURL", func() iostub {
return iostub{
addNil: func() error {
return WafWhiteUrlServiceApp.AddApi(request.WafAllowUrlAddReq{HostCode: "hc_nil", CompareType: "等于", Url: "/lsv-nil", Remarks: "x"})
},
addZero: func() error {
return WafWhiteUrlServiceApp.AddApi(request.WafAllowUrlAddReq{HostCode: "hc_zero", CompareType: "等于", Url: "/lsv-zero", Remarks: "x", IsEnable: &zero})
},
addOne: func() error {
return WafWhiteUrlServiceApp.AddApi(request.WafAllowUrlAddReq{HostCode: "hc_one", CompareType: "等于", Url: "/lsv-one", Remarks: "x", IsEnable: &one})
},
toggle: func(id string, v int) error {
val := v
_, err := WafWhiteUrlServiceApp.ToggleApi(request.WafAllowUrlToggleReq{Id: id, IsEnable: &val})
return err
},
findByHC: func(hostCode string) (int, error) {
var b model.URLAllowList
if err := db.Where("host_code = ?", hostCode).First(&b).Error; err != nil {
return -1, err
}
return b.IsEnable, nil
},
}
}},
{"LSV_BlockURL", func() iostub {
return iostub{
addNil: func() error {
return WafBlockUrlServiceApp.AddApi(request.WafBlockUrlAddReq{HostCode: "hc_nil", CompareType: "等于", Url: "/lsvb-nil", Remarks: "x"})
},
addZero: func() error {
return WafBlockUrlServiceApp.AddApi(request.WafBlockUrlAddReq{HostCode: "hc_zero", CompareType: "等于", Url: "/lsvb-zero", Remarks: "x", IsEnable: &zero})
},
addOne: func() error {
return WafBlockUrlServiceApp.AddApi(request.WafBlockUrlAddReq{HostCode: "hc_one", CompareType: "等于", Url: "/lsvb-one", Remarks: "x", IsEnable: &one})
},
toggle: func(id string, v int) error {
val := v
_, err := WafBlockUrlServiceApp.ToggleApi(request.WafBlockUrlToggleReq{Id: id, IsEnable: &val})
return err
},
findByHC: func(hostCode string) (int, error) {
var b model.URLBlockList
if err := db.Where("host_code = ?", hostCode).First(&b).Error; err != nil {
return -1, err
}
return b.IsEnable, nil
},
}
}},
}
for _, it := range mk {
it := it
t.Run(it.name, func(t *testing.T) {
k := it.kit()
fatalIf(t, k.addNil())
if v, err := k.findByHC("hc_nil"); err != nil || v != 1 {
t.Fatalf("新增未传开关字段应为1(启用),实际 %d err=%v", v, err)
}
fatalIf(t, k.addZero())
if v, err := k.findByHC("hc_zero"); err != nil || v != 0 {
t.Fatalf("新增显式0应为0(停用),实际 %d err=%v(default 标签吞 0?)", v, err)
}
fatalIf(t, k.addOne())
if v, err := k.findByHC("hc_one"); err != nil || v != 1 {
t.Fatalf("新增显式1应为1,实际 %d err=%v", v, err)
}
// toggle 往返:0 落库、1 落库
var b model.IPAllowList
_ = b
var id string
switch it.name {
case "LSV_AllowIP":
var r model.IPAllowList
firstBy(t, db, &r, "host_code = ?", "hc_zero")
id = r.Id
case "LSV_BlockIP":
var r model.IPBlockList
firstBy(t, db, &r, "host_code = ?", "hc_zero")
id = r.Id
case "LSV_AllowURL":
var r model.URLAllowList
firstBy(t, db, &r, "host_code = ?", "hc_zero")
id = r.Id
case "LSV_BlockURL":
var r model.URLBlockList
firstBy(t, db, &r, "host_code = ?", "hc_zero")
id = r.Id
}
fatalIf(t, k.toggle(id, 0))
if v, err := k.findByHC("hc_zero"); err != nil || v != 0 {
t.Fatalf("toggle 0 应落库 0,实际 %d err=%v", v, err)
}
fatalIf(t, k.toggle(id, 1))
if v, err := k.findByHC("hc_zero"); err != nil || v != 1 {
t.Fatalf("toggle 1 应落库 1,实际 %d err=%v", v, err)
}
})
}
// 语义锚:不带 default 标签的字段,裸结构体 Create 零值落库 0
// (批量导入等绕过 service 的插入路径依赖显式写 1;此用例防止有人加回 default 标签)
t.Run("BareCreateNoDefault", func(t *testing.T) {
hc := "hc_bare_" + sfx()
fatalIf(t, db.Create(&model.IPAllowList{BaseOrm: newBase(uuid.GenUUID()), HostCode: hc, Ip: "10.10.2.1"}).Error)
var b model.IPAllowList
firstBy(t, db, &b, "host_code = ?", hc)
if b.IsEnable != 0 {
t.Fatalf("裸 Create 零值应落库 0(无 default 标签语义),实际 %d", b.IsEnable)
}
})
}
+22 -1
View File
@@ -16,6 +16,10 @@ type WafWhiteIpService struct{}
var WafWhiteIpServiceApp = new(WafWhiteIpService)
func (receiver *WafWhiteIpService) AddApi(wafWhiteIpAddReq request.WafAllowIpAddReq) error {
isEnable := 1
if wafWhiteIpAddReq.IsEnable != nil {
isEnable = *wafWhiteIpAddReq.IsEnable
}
var wafHost = &model.IPAllowList{
BaseOrm: baseorm.BaseOrm{
Id: uuid.GenUUID(),
@@ -29,14 +33,28 @@ func (receiver *WafWhiteIpService) AddApi(wafWhiteIpAddReq request.WafAllowIpAdd
Remarks: wafWhiteIpAddReq.Remarks,
IpType: wafWhiteIpAddReq.IpType,
GroupCode: wafWhiteIpAddReq.GroupCode,
IsEnable: isEnable,
}
global.GWAF_LOCAL_DB.Create(wafHost)
return nil
}
// ToggleApi 切换条目启用状态,返回所属网站编码供调用方通知引擎
func (receiver *WafWhiteIpService) ToggleApi(req request.WafAllowIpToggleReq) (string, error) {
var bean model.IPAllowList
if err := global.GWAF_LOCAL_DB.Where("id = ?", req.Id).First(&bean).Error; err != nil {
return "", err
}
err := global.GWAF_LOCAL_DB.Model(model.IPAllowList{}).Where("id = ?", req.Id).Updates(map[string]interface{}{
"is_enable": *req.IsEnable,
"UPDATE_TIME": customtype.JsonTime(time.Now()),
}).Error
return bean.HostCode, err
}
// CheckIsExistApi 判重。唯一性由 (host_code, ip_type, ip, group_code) 共同决定:
// 同一站点重复引用同一个 IP 组也算重复。
// ip_type 用 IN ('', 'ip') 兼容存量行的空串。
// ip_type 用 IN (”, 'ip') 兼容存量行的空串。
func (receiver *WafWhiteIpService) CheckIsExistApi(wafWhiteIpAddReq request.WafAllowIpAddReq) error {
if wafWhiteIpAddReq.IpType == model.IPEntryTypeGroup {
return global.GWAF_LOCAL_DB.First(&model.IPAllowList{},
@@ -68,6 +86,9 @@ func (receiver *WafWhiteIpService) ModifyApi(wafWhiteIpEditReq request.WafAllowI
"group_code": wafWhiteIpEditReq.GroupCode,
"UPDATE_TIME": customtype.JsonTime(time.Now()),
}
if wafWhiteIpEditReq.IsEnable != nil {
ipWhiteMap["is_enable"] = *wafWhiteIpEditReq.IsEnable
}
err := global.GWAF_LOCAL_DB.Model(model.IPAllowList{}).Where("id = ?", wafWhiteIpEditReq.Id).Updates(ipWhiteMap).Error
return err
+21
View File
@@ -16,6 +16,10 @@ type WafWhiteUrlService struct{}
var WafWhiteUrlServiceApp = new(WafWhiteUrlService)
func (receiver *WafWhiteUrlService) AddApi(req request.WafAllowUrlAddReq) error {
isEnable := 1
if req.IsEnable != nil {
isEnable = *req.IsEnable
}
var bean = &model.URLAllowList{
BaseOrm: baseorm.BaseOrm{
Id: uuid.GenUUID(),
@@ -28,11 +32,25 @@ func (receiver *WafWhiteUrlService) AddApi(req request.WafAllowUrlAddReq) error
CompareType: req.CompareType,
Url: req.Url,
Remarks: req.Remarks,
IsEnable: isEnable,
}
global.GWAF_LOCAL_DB.Create(bean)
return nil
}
// ToggleApi 切换条目启用状态,返回所属网站编码供调用方通知引擎
func (receiver *WafWhiteUrlService) ToggleApi(req request.WafAllowUrlToggleReq) (string, error) {
var bean model.URLAllowList
if err := global.GWAF_LOCAL_DB.Where("id = ?", req.Id).First(&bean).Error; err != nil {
return "", err
}
err := global.GWAF_LOCAL_DB.Model(model.URLAllowList{}).Where("id = ?", req.Id).Updates(map[string]interface{}{
"is_enable": *req.IsEnable,
"UPDATE_TIME": customtype.JsonTime(time.Now()),
}).Error
return bean.HostCode, err
}
func (receiver *WafWhiteUrlService) CheckIsExistApi(req request.WafAllowUrlAddReq) error {
return global.GWAF_LOCAL_DB.First(&model.URLAllowList{}, "host_code = ? and url= ?", req.HostCode,
req.Url).Error
@@ -51,6 +69,9 @@ func (receiver *WafWhiteUrlService) ModifyApi(req request.WafAllowUrlEditReq) er
"Remarks": req.Remarks,
"UPDATE_TIME": customtype.JsonTime(time.Now()),
}
if req.IsEnable != nil {
ipWhiteMap["is_enable"] = *req.IsEnable
}
err := global.GWAF_LOCAL_DB.Model(model.URLAllowList{}).Where("id = ?", req.Id).Updates(ipWhiteMap).Error
return err
+22 -1
View File
@@ -16,6 +16,10 @@ type WafBlockIpService struct{}
var WafBlockIpServiceApp = new(WafBlockIpService)
func (receiver *WafBlockIpService) AddApi(req request.WafBlockIpAddReq) error {
isEnable := 1
if req.IsEnable != nil {
isEnable = *req.IsEnable
}
var bean = &model.IPBlockList{
BaseOrm: baseorm.BaseOrm{
Id: uuid.GenUUID(),
@@ -29,14 +33,28 @@ func (receiver *WafBlockIpService) AddApi(req request.WafBlockIpAddReq) error {
Remarks: req.Remarks,
IpType: req.IpType,
GroupCode: req.GroupCode,
IsEnable: isEnable,
}
global.GWAF_LOCAL_DB.Create(bean)
return nil
}
// ToggleApi 切换条目启用状态,返回所属网站编码供调用方通知引擎
func (receiver *WafBlockIpService) ToggleApi(req request.WafBlockIpToggleReq) (string, error) {
var bean model.IPBlockList
if err := global.GWAF_LOCAL_DB.Where("id = ?", req.Id).First(&bean).Error; err != nil {
return "", err
}
err := global.GWAF_LOCAL_DB.Model(model.IPBlockList{}).Where("id = ?", req.Id).Updates(map[string]interface{}{
"is_enable": *req.IsEnable,
"UPDATE_TIME": customtype.JsonTime(time.Now()),
}).Error
return bean.HostCode, err
}
// CheckIsExistApi 判重。唯一性由 (host_code, ip_type, ip, group_code) 共同决定:
// 同一站点重复引用同一个 IP 组也算重复。
// ip_type 用 IN ('', 'ip') 兼容存量行的空串。
// ip_type 用 IN (”, 'ip') 兼容存量行的空串。
func (receiver *WafBlockIpService) CheckIsExistApi(req request.WafBlockIpAddReq) error {
if req.IpType == model.IPEntryTypeGroup {
return global.GWAF_LOCAL_DB.First(&model.IPBlockList{},
@@ -67,6 +85,9 @@ func (receiver *WafBlockIpService) ModifyApi(req request.WafBlockIpEditReq) erro
"group_code": req.GroupCode,
"UPDATE_TIME": customtype.JsonTime(time.Now()),
}
if req.IsEnable != nil {
ipWhiteMap["is_enable"] = *req.IsEnable
}
err := global.GWAF_LOCAL_DB.Model(model.IPBlockList{}).Where("id = ?", req.Id).Updates(ipWhiteMap).Error
return err
+21
View File
@@ -16,6 +16,10 @@ type WafBlockUrlService struct{}
var WafBlockUrlServiceApp = new(WafBlockUrlService)
func (receiver *WafBlockUrlService) AddApi(req request.WafBlockUrlAddReq) error {
isEnable := 1
if req.IsEnable != nil {
isEnable = *req.IsEnable
}
var bean = &model.URLBlockList{
BaseOrm: baseorm.BaseOrm{
Id: uuid.GenUUID(),
@@ -28,11 +32,25 @@ func (receiver *WafBlockUrlService) AddApi(req request.WafBlockUrlAddReq) error
Url: req.Url,
CompareType: req.CompareType,
Remarks: req.Remarks,
IsEnable: isEnable,
}
global.GWAF_LOCAL_DB.Create(bean)
return nil
}
// ToggleApi 切换条目启用状态,返回所属网站编码供调用方通知引擎
func (receiver *WafBlockUrlService) ToggleApi(req request.WafBlockUrlToggleReq) (string, error) {
var bean model.URLBlockList
if err := global.GWAF_LOCAL_DB.Where("id = ?", req.Id).First(&bean).Error; err != nil {
return "", err
}
err := global.GWAF_LOCAL_DB.Model(model.URLBlockList{}).Where("id = ?", req.Id).Updates(map[string]interface{}{
"is_enable": *req.IsEnable,
"UPDATE_TIME": customtype.JsonTime(time.Now()),
}).Error
return bean.HostCode, err
}
func (receiver *WafBlockUrlService) CheckIsExistApi(req request.WafBlockUrlAddReq) error {
return global.GWAF_LOCAL_DB.First(&model.URLBlockList{}, "host_code = ? and url= ?", req.HostCode,
req.Url).Error
@@ -51,6 +69,9 @@ func (receiver *WafBlockUrlService) ModifyApi(req request.WafBlockUrlEditReq) er
"CompareType": req.CompareType,
"UPDATE_TIME": customtype.JsonTime(time.Now()),
}
if req.IsEnable != nil {
modfiyMap["is_enable"] = *req.IsEnable
}
err := global.GWAF_LOCAL_DB.Model(model.URLBlockList{}).Where("id = ?", req.Id).Updates(modfiyMap).Error
return err
+6 -4
View File
@@ -255,7 +255,8 @@ func (r *WafIPLookupService) hostLabel(code string, names map[string]string) str
func (r *WafIPLookupService) matchAllowList(ip string, names map[string]string, resp *response2.IPLookupResp) {
var rows []model.IPAllowList
if err := global.GWAF_LOCAL_DB.Find(&rows).Error; err != nil {
//与引擎同口径:停用条目不参与判定,诊断结论才不会背离实际拦截行为
if err := global.GWAF_LOCAL_DB.Where("is_enable = 1").Find(&rows).Error; err != nil {
resp.Degraded = append(resp.Degraded, srcIPWhite)
return
}
@@ -279,7 +280,7 @@ func (r *WafIPLookupService) matchAllowList(ip string, names map[string]string,
func (r *WafIPLookupService) matchBlockList(ip string, names map[string]string, resp *response2.IPLookupResp) {
var rows []model.IPBlockList
if err := global.GWAF_LOCAL_DB.Find(&rows).Error; err != nil {
if err := global.GWAF_LOCAL_DB.Where("is_enable = 1").Find(&rows).Error; err != nil {
resp.Degraded = append(resp.Degraded, srcIPBlack)
return
}
@@ -366,7 +367,8 @@ func (r *WafIPLookupService) groupRefs() map[string]groupRef {
refs := map[string]groupRef{}
var blockRows []model.IPBlockList
global.GWAF_LOCAL_DB.Where("ip_type = ?", model.IPEntryTypeGroup).Find(&blockRows)
//引用统计同样只算启用条目,与引擎侧组短码抽取同口径
global.GWAF_LOCAL_DB.Where("ip_type = ? and is_enable = 1", model.IPEntryTypeGroup).Find(&blockRows)
for _, row := range blockRows {
if row.GroupCode == "" {
continue
@@ -375,7 +377,7 @@ func (r *WafIPLookupService) groupRefs() map[string]groupRef {
}
var allowRows []model.IPAllowList
global.GWAF_LOCAL_DB.Where("ip_type = ?", model.IPEntryTypeGroup).Find(&allowRows)
global.GWAF_LOCAL_DB.Where("ip_type = ? and is_enable = 1", model.IPEntryTypeGroup).Find(&allowRows)
for _, row := range allowRows {
if row.GroupCode == "" {
continue
+48
View File
@@ -2406,6 +2406,54 @@ func RunCoreDBMigrations(db *gorm.DB) error {
return nil
},
},
// 迁移: 四类名单表(IP/URL 黑白名单)新增条目级启用开关列。
// 存量行回填 1(启用),升级前后生效行为不变
{
ID: "202610210001_add_list_entry_is_enable",
Migrate: func(tx *gorm.DB) error {
zlog.Info("迁移 202610210001: 四类名单表新增条目启用开关列")
type listCol struct {
model any
field string
col string
}
cols := []listCol{
{&model.IPAllowList{}, "IsEnable", "is_enable"},
{&model.URLAllowList{}, "IsEnable", "is_enable"},
{&model.IPBlockList{}, "IsEnable", "is_enable"},
{&model.URLBlockList{}, "IsEnable", "is_enable"},
}
for _, c := range cols {
if tx.Migrator().HasColumn(c.model, c.col) {
continue
}
if err := tx.Migrator().AddColumn(c.model, c.field); err != nil {
return fmt.Errorf("新增名单条目启用列失败: %w", err)
}
}
// 只回填 NULL:0 是「用户主动停用」的合法值,不能动
for _, c := range cols {
if err := tx.Model(c.model).Where("is_enable IS NULL").Update("is_enable", 1).Error; err != nil {
return fmt.Errorf("回填名单条目启用默认值失败: %w", err)
}
}
zlog.Info("迁移 202610210001: 完成")
return nil
},
Rollback: func(tx *gorm.DB) error {
zlog.Info("回滚 202610210001: 删除四类名单表的条目启用开关列")
// 注意:删列即丢弃开关状态——用户停用过的条目回滚后回到「全部生效」,
// 再升级也不会恢复停用标记
for _, m := range []any{&model.IPAllowList{}, &model.URLAllowList{}, &model.IPBlockList{}, &model.URLBlockList{}} {
if tx.Migrator().HasColumn(m, "is_enable") {
if err := tx.Migrator().DropColumn(m, "is_enable"); err != nil {
return err
}
}
}
return nil
},
},
})
// 执行迁移
+5 -5
View File
@@ -120,21 +120,21 @@ func (waf *WafEngine) LoadHost(inHost model.Hosts) []innerbean.ServerRunTime {
//加载并编译CC多规则(编译放在加载期,请求期只做取值与比较)
ccRules := LoadCompiledCCRules(inHost.Code)
//查询ip白名单
//查询ip白名单(停用条目不下发引擎,下同)
var ipwhitelist []model.IPAllowList
global.GWAF_LOCAL_DB.Where("host_code=? ", inHost.Code).Find(&ipwhitelist)
global.GWAF_LOCAL_DB.Where("host_code=? and is_enable = 1", inHost.Code).Find(&ipwhitelist)
//查询url白名单
var urlwhitelist []model.URLAllowList
global.GWAF_LOCAL_DB.Where("host_code=? ", inHost.Code).Find(&urlwhitelist)
global.GWAF_LOCAL_DB.Where("host_code=? and is_enable = 1", inHost.Code).Find(&urlwhitelist)
//查询ip黑名单
var ipblocklist []model.IPBlockList
global.GWAF_LOCAL_DB.Where("host_code=? ", inHost.Code).Find(&ipblocklist)
global.GWAF_LOCAL_DB.Where("host_code=? and is_enable = 1", inHost.Code).Find(&ipblocklist)
//查询url白名单
var urlblocklist []model.URLBlockList
global.GWAF_LOCAL_DB.Where("host_code=? ", inHost.Code).Find(&urlblocklist)
global.GWAF_LOCAL_DB.Where("host_code=? and is_enable = 1", inHost.Code).Find(&urlblocklist)
//查询url隐私保护
var ldpurls []model.LDPUrl
+5 -1
View File
@@ -57,7 +57,7 @@ func (p *IPAllowProcessor) GetExistingItems(items []string, task model.BatchTask
// NotifyEngine 通知引擎更新
func (p *IPAllowProcessor) NotifyEngine(task model.BatchTask) {
var ipWhites []model.IPAllowList
global.GWAF_LOCAL_DB.Where("host_code = ? ", task.BatchHostCode).Find(&ipWhites)
global.GWAF_LOCAL_DB.Where("host_code = ? and is_enable = 1", task.BatchHostCode).Find(&ipWhites)
var chanInfo = spec.ChanCommonHost{
HostCode: task.BatchHostCode,
Type: enums.ChanTypeAllowIP,
@@ -86,6 +86,8 @@ func (p *IPAllowProcessor) processAppendBatch(items []string, existMap map[strin
Remarks: time.Now().Format("20060102") + "批量导入 任务ID:" + task.Id,
// 显式标记为「单条IP」:批量导入不产生 IP 组引用
IpType: model.IPEntryTypeIP,
// 显式启用:字段无 default 标签,零值会被如实落库为停用
IsEnable: 1,
})
}
}
@@ -137,6 +139,8 @@ func (p *IPAllowProcessor) processOverwriteBatch(items []string, existMap map[st
Remarks: time.Now().Format("20060102") + "批量导入 任务ID:" + task.Id,
// 显式标记为「单条IP」:批量导入不产生 IP 组引用
IpType: model.IPEntryTypeIP,
// 显式启用:字段无 default 标签,零值会被如实落库为停用
IsEnable: 1,
})
}
}
+5 -1
View File
@@ -57,7 +57,7 @@ func (p *IPDenyProcessor) GetExistingItems(items []string, task model.BatchTask,
// NotifyEngine 通知引擎更新
func (p *IPDenyProcessor) NotifyEngine(task model.BatchTask) {
var ipBlocks []model.IPBlockList
global.GWAF_LOCAL_DB.Where("host_code = ? ", task.BatchHostCode).Find(&ipBlocks)
global.GWAF_LOCAL_DB.Where("host_code = ? and is_enable = 1", task.BatchHostCode).Find(&ipBlocks)
var chanInfo = spec.ChanCommonHost{
HostCode: task.BatchHostCode,
Type: enums.ChanTypeBlockIP,
@@ -86,6 +86,8 @@ func (p *IPDenyProcessor) processAppendBatch(items []string, existMap map[string
Remarks: time.Now().Format("20060102") + "批量导入 任务ID:" + task.Id,
// 显式标记为「单条IP」:批量导入不产生 IP 组引用
IpType: model.IPEntryTypeIP,
// 显式启用:字段无 default 标签,零值会被如实落库为停用
IsEnable: 1,
})
}
}
@@ -137,6 +139,8 @@ func (p *IPDenyProcessor) processOverwriteBatch(items []string, existMap map[str
Remarks: time.Now().Format("20060102") + "批量导入 任务ID:" + task.Id,
// 显式标记为「单条IP」:批量导入不产生 IP 组引用
IpType: model.IPEntryTypeIP,
// 显式启用:字段无 default 标签,零值会被如实落库为停用
IsEnable: 1,
})
}
}
+24
View File
@@ -12,6 +12,30 @@
# 5. fresh_install: true 的条目只在"全新安装"时生成,不参与版本区间计算
notes:
- id: v1_3_25_list_entry_switches
version: v1.3.25
kind: check
level: low
page: /waf/wafipwhitelist
apply:
type: navigate
zh:
title: IP/URL 黑白名单支持按条目启用停用
detail: >-
IP白名单、URL白名单、IP黑名单、URL黑名单的每条记录新增启用开关,在列表中直接切换,
新增条目默认启用,存量条目升级后保持启用。停用的条目不再参与对应站点的防护判定
(全局名单同理),配置仍保留在列表中,随时可重新启用;批量和编辑等原有操作不受影响。
revert: 在对应名单页面把条目开关重新打开,即恢复该条目的防护效果。
en:
title: Per-entry enable switches for IP/URL black- and whitelists
detail: >-
Each entry in the IP whitelist, URL whitelist, IP blacklist and URL blacklist now has an
enable switch toggled directly in the list. New entries default to enabled and existing
entries stay enabled after the upgrade. Disabled entries stop taking effect for the
corresponding website (global entries likewise) but remain in the list and can be
re-enabled at any time; batch import and editing behave as before.
revert: Turn the entry switch back on in the corresponding list page to restore its effect.
- id: v1_3_25_rule_ban
version: v1.3.25
kind: check