diff --git a/internal/rpc/rpc_client.go b/internal/rpc/rpc_client.go index eff4702b..7c8ced9e 100644 --- a/internal/rpc/rpc_client.go +++ b/internal/rpc/rpc_client.go @@ -176,6 +176,14 @@ func (this *RPCClient) IPLibraryRPC() pb.IPLibraryServiceClient { return pb.NewIPLibraryServiceClient(this.pickConn()) } +func (this *RPCClient) IPListRPC() pb.IPListServiceClient { + return pb.NewIPListServiceClient(this.pickConn()) +} + +func (this *RPCClient) IPItemRPC() pb.IPItemServiceClient { + return pb.NewIPItemServiceClient(this.pickConn()) +} + func (this *RPCClient) FileRPC() pb.FileServiceClient { return pb.NewFileServiceClient(this.pickConn()) } diff --git a/internal/web/actions/default/servers/components/waf/init.go b/internal/web/actions/default/servers/components/waf/init.go index 04d03efb..29323d0a 100644 --- a/internal/web/actions/default/servers/components/waf/init.go +++ b/internal/web/actions/default/servers/components/waf/init.go @@ -41,6 +41,9 @@ func init() { GetPost("/ipadmin", new(ipadmin.IndexAction)). GetPost("/ipadmin/provinces", new(ipadmin.ProvincesAction)). Get("/ipadmin/lists", new(ipadmin.ListsAction)). + GetPost("/ipadmin/createIPPopup", new(ipadmin.CreateIPPopupAction)). + GetPost("/ipadmin/updateIPPopup", new(ipadmin.UpdateIPPopupAction)). + Post("/ipadmin/deleteIP", new(ipadmin.DeleteIPAction)). EndAll() }) diff --git a/internal/web/actions/default/servers/components/waf/ipadmin/createIPPopup.go b/internal/web/actions/default/servers/components/waf/ipadmin/createIPPopup.go new file mode 100644 index 00000000..df78b125 --- /dev/null +++ b/internal/web/actions/default/servers/components/waf/ipadmin/createIPPopup.go @@ -0,0 +1,64 @@ +package ipadmin + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/models" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/actions" +) + +type CreateIPPopupAction struct { + actionutils.ParentAction +} + +func (this *CreateIPPopupAction) Init() { + this.Nav("", "", "") +} + +func (this *CreateIPPopupAction) RunGet(params struct { + FirewallPolicyId int64 + Type string +}) { + this.Data["type"] = params.Type + + listId, err := models.SharedHTTPFirewallPolicyDAO.FindEnabledPolicyIPListIdWithType(this.AdminContext(), params.FirewallPolicyId, params.Type) + if err != nil { + this.ErrorPage(err) + return + } + this.Data["listId"] = listId + + this.Show() +} + +func (this *CreateIPPopupAction) RunPost(params struct { + ListId int64 + IpFrom string + IpTo string + ExpiredAt int64 + Reason string + + Must *actions.Must + CSRF *actionutils.CSRF +}) { + // TODO 校验ListId所属用户 + // TODO 校验IP格式(ipFrom/ipTo) + + params.Must. + Field("ipFrom", params.IpFrom). + Require("请输入开始IP") + + _, err := this.RPC().IPItemRPC().CreateIPItem(this.AdminContext(), &pb.CreateIPItemRequest{ + IpListId: params.ListId, + IpFrom: params.IpFrom, + IpTo: params.IpTo, + ExpiredAt: params.ExpiredAt, + Reason: params.Reason, + }) + if err != nil { + this.ErrorPage(err) + return + } + + this.Success() +} diff --git a/internal/web/actions/default/servers/components/waf/ipadmin/deleteIP.go b/internal/web/actions/default/servers/components/waf/ipadmin/deleteIP.go new file mode 100644 index 00000000..c11d3d8e --- /dev/null +++ b/internal/web/actions/default/servers/components/waf/ipadmin/deleteIP.go @@ -0,0 +1,24 @@ +package ipadmin + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" +) + +type DeleteIPAction struct { + actionutils.ParentAction +} + +func (this *DeleteIPAction) RunPost(params struct { + ItemId int64 +}) { + // TODO 判断权限 + + _, err := this.RPC().IPItemRPC().DeleteIPItem(this.AdminContext(), &pb.DeleteIPItemRequest{IpItemId: params.ItemId}) + if err != nil { + this.ErrorPage(err) + return + } + + this.Success() +} diff --git a/internal/web/actions/default/servers/components/waf/ipadmin/lists.go b/internal/web/actions/default/servers/components/waf/ipadmin/lists.go index a67633c3..3070ef68 100644 --- a/internal/web/actions/default/servers/components/waf/ipadmin/lists.go +++ b/internal/web/actions/default/servers/components/waf/ipadmin/lists.go @@ -1,6 +1,12 @@ package ipadmin -import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/models" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/maps" + timeutil "github.com/iwind/TeaGo/utils/time" +) type ListsAction struct { actionutils.ParentAction @@ -10,8 +16,56 @@ func (this *ListsAction) Init() { this.Nav("", "", "ipadmin") } -func (this *ListsAction) RunGet(params struct{}) { - this.Data["subMenuItem"] = "list" +func (this *ListsAction) RunGet(params struct { + FirewallPolicyId int64 + Type string +}) { + this.Data["subMenuItem"] = params.Type + + listId, err := models.SharedHTTPFirewallPolicyDAO.FindEnabledPolicyIPListIdWithType(this.AdminContext(), params.FirewallPolicyId, params.Type) + if err != nil { + this.ErrorPage(err) + return + } + + this.Data["listId"] = listId + + // 数量 + countResp, err := this.RPC().IPItemRPC().CountIPItemsWithListId(this.AdminContext(), &pb.CountIPItemsWithListIdRequest{IpListId: listId}) + if err != nil { + this.ErrorPage(err) + return + } + count := countResp.Count + page := this.NewPage(count) + this.Data["page"] = page.AsHTML() + + // 列表 + itemsResp, err := this.RPC().IPItemRPC().ListIPItemsWithListId(this.AdminContext(), &pb.ListIPItemsWithListIdRequest{ + IpListId: listId, + Offset: page.Offset, + Size: page.Size, + }) + if err != nil { + this.ErrorPage(err) + return + } + itemMaps := []maps.Map{} + for _, item := range itemsResp.IpItems { + expiredTime := "" + if item.ExpiredAt > 0 { + expiredTime = timeutil.FormatTime("Y-m-d H:i:s", item.ExpiredAt) + } + + itemMaps = append(itemMaps, maps.Map{ + "id": item.Id, + "ipFrom": item.IpFrom, + "ipTo": item.IpTo, + "expiredTime": expiredTime, + "reason": item.Reason, + }) + } + this.Data["items"] = itemMaps this.Show() } diff --git a/internal/web/actions/default/servers/components/waf/ipadmin/updateIPPopup.go b/internal/web/actions/default/servers/components/waf/ipadmin/updateIPPopup.go new file mode 100644 index 00000000..bd020439 --- /dev/null +++ b/internal/web/actions/default/servers/components/waf/ipadmin/updateIPPopup.go @@ -0,0 +1,74 @@ +package ipadmin + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/actions" + "github.com/iwind/TeaGo/maps" +) + +type UpdateIPPopupAction struct { + actionutils.ParentAction +} + +func (this *UpdateIPPopupAction) Init() { + this.Nav("", "", "") +} + +func (this *UpdateIPPopupAction) RunGet(params struct { + ItemId int64 +}) { + itemResp, err := this.RPC().IPItemRPC().FindEnabledIPItem(this.AdminContext(), &pb.FindEnabledIPItemRequest{IpItemId: params.ItemId}) + if err != nil { + this.ErrorPage(err) + return + } + item := itemResp.IpItem + if item == nil { + this.NotFound("ipItem", params.ItemId) + return + } + + this.Data["item"] = maps.Map{ + "id": item.Id, + "ipFrom": item.IpFrom, + "ipTo": item.IpTo, + "expiredAt": item.ExpiredAt, + "reason": item.Reason, + } + + this.Show() +} + +func (this *UpdateIPPopupAction) RunPost(params struct { + ItemId int64 + + IpFrom string + IpTo string + ExpiredAt int64 + Reason string + + Must *actions.Must + CSRF *actionutils.CSRF +}) { + // TODO 校验ItemId所属用户 + // TODO 校验IP格式(ipFrom/ipTo) + + params.Must. + Field("ipFrom", params.IpFrom). + Require("请输入开始IP") + + _, err := this.RPC().IPItemRPC().UpdateIPItem(this.AdminContext(), &pb.UpdateIPItemRequest{ + IpItemId: params.ItemId, + IpFrom: params.IpFrom, + IpTo: params.IpTo, + ExpiredAt: params.ExpiredAt, + Reason: params.Reason, + }) + if err != nil { + this.ErrorPage(err) + return + } + + this.Success() +} diff --git a/internal/web/models/http_firewall_policy_dao.go b/internal/web/models/http_firewall_policy_dao.go index a29d418b..ec6ff02c 100644 --- a/internal/web/models/http_firewall_policy_dao.go +++ b/internal/web/models/http_firewall_policy_dao.go @@ -3,17 +3,20 @@ package models import ( "context" "encoding/json" + "github.com/TeaOSLab/EdgeAdmin/internal/errors" "github.com/TeaOSLab/EdgeAdmin/internal/rpc" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ipconfigs" ) var SharedHTTPFirewallPolicyDAO = new(HTTPFirewallPolicyDAO) +// WAF策略相关 type HTTPFirewallPolicyDAO struct { } -// 查找缓存策略基本信息 +// 查找WAF策略基本信息 func (this *HTTPFirewallPolicyDAO) FindEnabledPolicy(ctx context.Context, policyId int64) (*pb.HTTPFirewallPolicy, error) { client, err := rpc.SharedRPC() if err != nil { @@ -26,7 +29,7 @@ func (this *HTTPFirewallPolicyDAO) FindEnabledPolicy(ctx context.Context, policy return resp.FirewallPolicy, nil } -// 查找缓存策略配置 +// 查找WAF策略配置 func (this *HTTPFirewallPolicyDAO) FindEnabledPolicyConfig(ctx context.Context, policyId int64) (*firewallconfigs.HTTPFirewallPolicy, error) { client, err := rpc.SharedRPC() if err != nil { @@ -46,3 +49,125 @@ func (this *HTTPFirewallPolicyDAO) FindEnabledPolicyConfig(ctx context.Context, } return firewallPolicy, nil } + +// 查找WAF的Inbound +func (this *HTTPFirewallPolicyDAO) FindEnabledPolicyInboundConfig(ctx context.Context, policyId int64) (*firewallconfigs.HTTPFirewallInboundConfig, error) { + config, err := this.FindEnabledPolicyConfig(ctx, policyId) + if err != nil { + return nil, err + } + if config == nil { + return nil, errors.New("not found") + } + return config.Inbound, nil +} + +// 根据类型查找WAF的IP名单 +func (this *HTTPFirewallPolicyDAO) FindEnabledPolicyIPListIdWithType(ctx context.Context, policyId int64, listType ipconfigs.IPListType) (int64, error) { + switch listType { + case ipconfigs.IPListTypeWhite: + return this.FindEnabledPolicyWhiteIPListId(ctx, policyId) + case ipconfigs.IPListTypeBlack: + return this.FindEnabledPolicyBlackIPListId(ctx, policyId) + default: + return 0, errors.New("invalid ip list type '" + listType + "'") + } +} + +// 查找WAF的白名单 +func (this *HTTPFirewallPolicyDAO) FindEnabledPolicyWhiteIPListId(ctx context.Context, policyId int64) (int64, error) { + client, err := rpc.SharedRPC() + if err != nil { + return 0, err + } + + config, err := this.FindEnabledPolicyConfig(ctx, policyId) + if err != nil { + return 0, err + } + if config == nil { + return 0, errors.New("not found") + } + if config.Inbound == nil { + config.Inbound = &firewallconfigs.HTTPFirewallInboundConfig{IsOn: true} + } + if config.Inbound.WhiteListRef == nil || config.Inbound.WhiteListRef.ListId == 0 { + createResp, err := client.IPListRPC().CreateIPList(ctx, &pb.CreateIPListRequest{ + Type: "white", + Name: "白名单", + Code: "white", + TimeoutJSON: nil, + }) + if err != nil { + return 0, err + } + listId := createResp.IpListId + config.Inbound.WhiteListRef = &ipconfigs.IPListRef{ + IsOn: true, + ListId: listId, + } + inboundJSON, err := json.Marshal(config.Inbound) + if err != nil { + return 0, err + } + _, err = client.HTTPFirewallPolicyRPC().UpdateHTTPFirewallInboundConfig(ctx, &pb.UpdateHTTPFirewallInboundConfigRequest{ + FirewallPolicyId: policyId, + InboundJSON: inboundJSON, + }) + if err != nil { + return 0, err + } + return listId, nil + } + + return config.Inbound.WhiteListRef.ListId, nil +} + +// 查找WAF的黑名单 +func (this *HTTPFirewallPolicyDAO) FindEnabledPolicyBlackIPListId(ctx context.Context, policyId int64) (int64, error) { + client, err := rpc.SharedRPC() + if err != nil { + return 0, err + } + + config, err := this.FindEnabledPolicyConfig(ctx, policyId) + if err != nil { + return 0, err + } + if config == nil { + return 0, errors.New("not found") + } + if config.Inbound == nil { + config.Inbound = &firewallconfigs.HTTPFirewallInboundConfig{IsOn: true} + } + if config.Inbound.BlackListRef == nil || config.Inbound.BlackListRef.ListId == 0 { + createResp, err := client.IPListRPC().CreateIPList(ctx, &pb.CreateIPListRequest{ + Type: "black", + Name: "黑名单", + Code: "black", + TimeoutJSON: nil, + }) + if err != nil { + return 0, err + } + listId := createResp.IpListId + config.Inbound.BlackListRef = &ipconfigs.IPListRef{ + IsOn: true, + ListId: listId, + } + inboundJSON, err := json.Marshal(config.Inbound) + if err != nil { + return 0, err + } + _, err = client.HTTPFirewallPolicyRPC().UpdateHTTPFirewallInboundConfig(ctx, &pb.UpdateHTTPFirewallInboundConfigRequest{ + FirewallPolicyId: policyId, + InboundJSON: inboundJSON, + }) + if err != nil { + return 0, err + } + return listId, nil + } + + return config.Inbound.BlackListRef.ListId, nil +} diff --git a/web/public/js/components/common/datetime-input.js b/web/public/js/components/common/datetime-input.js new file mode 100644 index 00000000..ad2d98d8 --- /dev/null +++ b/web/public/js/components/common/datetime-input.js @@ -0,0 +1,150 @@ +Vue.component("datetime-input", { + props: ["v-name", "v-timestamp"], + data: function () { + let timestamp = this.vTimestamp + if (timestamp != null) { + timestamp = parseInt(timestamp) + if (isNaN(timestamp)) { + timestamp = 0 + } + } else { + timestamp = 0 + } + + let day = "" + let hour = "" + let minute = "" + let second = "" + + if (timestamp > 0) { + let date = new Date() + date.setTime(timestamp * 1000) + + let year = date.getFullYear().toString() + let month = this.leadingZero((date.getMonth() + 1).toString(), 2) + day = year + "-" + month + "-" + this.leadingZero(date.getDate().toString(), 2) + + hour = this.leadingZero(date.getHours().toString(), 2) + minute = this.leadingZero(date.getMinutes().toString(), 2) + second = this.leadingZero(date.getSeconds().toString(), 2) + } + + return { + timestamp: timestamp, + day: day, + hour: hour, + minute: minute, + second: second, + + hasDayError: false, + hasHourError: false, + hasMinuteError: false, + hasSecondError: false + } + }, + methods: { + change: function () { + let date = new Date() + + // day + if (!/^\d{4}-\d{1,2}-\d{1,2}$/.test(this.day)) { + this.hasDayError = true + return + } + let pieces = this.day.split("-") + let year = parseInt(pieces[0]) + date.setFullYear(year) + + let month = parseInt(pieces[1]) + if (month < 1 || month > 12) { + this.hasDayError = true + return + } + date.setMonth(month - 1) + + let day = parseInt(pieces[2]) + if (day < 1 || day > 32) { + this.hasDayError = true + return + } + date.setDate(day) + + this.hasDayError = false + + // hour + if (!/^\d+$/.test(this.hour)) { + this.hasHourError = true + return + } + let hour = parseInt(this.hour) + if (isNaN(hour)) { + this.hasHourError = true + return + } + if (hour < 0 || hour >= 24) { + this.hasHourError = true + return + } + this.hasHourError = false + date.setHours(hour) + + // minute + if (!/^\d+$/.test(this.minute)) { + this.hasMinuteError = true + return + } + let minute = parseInt(this.minute) + if (isNaN(minute)) { + this.hasMinuteError = true + return + } + if (minute < 0 || minute >= 60) { + this.hasMinuteError = true + return + } + this.hasMinuteError = false + date.setMinutes(minute) + + // second + if (!/^\d+$/.test(this.second)) { + this.hasSecondError = true + return + } + let second = parseInt(this.second) + if (isNaN(second)) { + this.hasSecondError = true + return + } + if (second < 0 || second >= 60) { + this.hasSecondError = true + return + } + this.hasSecondError = false + date.setSeconds(second) + + this.timestamp = Math.ceil(date.getTime() / 1000) + }, + leadingZero: function (s, l) { + if (l <= s.length) { + return s + } + for (let i = 0; i < l - s.length; i++) { + s = "0" + s + } + return s + } + }, + template: `
暂时还没有IP。
+ +| IP | +过期时间 | +备注 | +操作 | +
|---|---|---|---|
| {{item.ipFrom}} - {{item.ipTo}} | ++ {{item.expiredTime}} + 不过期 + | ++ {{item.reason}} + - + | ++ 修改 + 删除 + | +