实现WAF部分功能

This commit is contained in:
刘祥超
2020-10-08 11:11:37 +08:00
parent 62b9ab9f5d
commit 66d90807db
28 changed files with 1443 additions and 17 deletions

View File

@@ -0,0 +1,86 @@
{$layout "layout_popup"}
<h3 v-if="!isUpdating">添加规则</h3>
<h3 v-if="isUpdating">修改规则</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="ruleId" :value="rule.id"/>
<input type="hidden" name="optionsJSON" v-if="checkpoint != null && checkpoint.options != null" :value="JSON.stringify(checkpoint.options)"/>
<table class="ui table definition selectable">
<tr>
<td class="title">参数</td>
<td>
<select name="prefix" class="ui dropdown auto-width" @change="changeCheckpoint()" v-model="rule.checkpointPrefix">
<option value="">[选择参数]</option>
<option v-for="cp in checkpoints" :value="cp.prefix">{{cp.name}} - [ {{cp.prefix}}]</option>
</select>
<p class="comment" v-if="checkpoint != null"><span class="ui label tiny">${<em style="font-style: normal;">{{checkpoint.prefix}}</em>}</span>{{checkpoint.description}}</p>
</td>
</tr>
<tr v-if="checkpoint != null && checkpoint.hasParams">
<td>参数名</td>
<td>
<select name="param" v-model="rule.checkpointParam" class="ui dropdown auto-width" v-if="checkpoint.params != null">
<option v-for="o in checkpoint.params" :value="o.value">{{o.name}}</option>
</select>
<input type="text" maxlength="100" v-model="rule.checkpointParam" v-if="checkpoint.params == null"/>
</td>
</tr>
<!-- 选项 -->
<tbody v-if="checkpoint != null && checkpoint.options != null && checkpoint.options.length > 0">
<tr v-for="option in checkpoint.options">
<td>{{option.name}}</td>
<td>
<div class="ui fields inline" v-if="option.type == 'field'">
<div class="ui field">
<input type="text" name="" :placeholder="option.placeholder" :maxlength="(option.maxLength > 0)?option.maxLength:1024" :size="option.size" v-model="option.value"/>
</div>
<div class="ui field">
{{option.rightLabel}}
</div>
</div>
<div class="ui fields inline" v-if="option.type == 'options'">
<div class="ui field">
<select class="ui dropdown" :style="'width:' + option.size + 'em'" name="" v-model="option.value">
<option v-for="opt in option.options" :value="opt.value">{{opt.name}}</option>
</select>
</div>
<div class="ui field">
{{option.rightLabel}}
</div>
</div>
<p class="comment" v-if="option.comment != null && option.comment.length > 0">{{option.comment}}</p>
</td>
</tr>
</tbody>
<tr>
<td>操作符</td>
<td>
<select class="ui dropdown" name="operator" style="width:10em" v-model="rule.operator" @change="changeOperator()">
<option v-for="op in operators" :value="op.code">{{op.name}}</option>
</select>
<p class="comment" v-if="operator != null" v-html="operator.description"></p>
</td>
</tr>
<tr v-if="operator.case != 'none'">
<td>开启大小写不敏感</td>
<td>
<div class="ui checkbox">
<input name="case" type="checkbox" value="1" v-model="rule.isCaseInsensitive"/>
<label></label>
</div>
<p class="comment">开启后忽略英文字母大小写</p>
</td>
</tr>
<tr>
<td>对比值</td>
<td>
<textarea rows="3" maxlength="4096" name="value" v-model="rule.value"></textarea>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,70 @@
Tea.context(function () {
this.success = NotifyPopup
this.isUpdating = (window.parent.UPDATING_RULE != null)
this.rule = {
id: 0,
param: "",
checkpointPrefix: "",
checkpointParam: "",
value: "",
isCaseInsensitive: false,
operator: "match",
checkpointOptions: null,
description: "",
isOn: true
}
if (window.parent.UPDATING_RULE != null) {
this.rule = window.parent.UPDATING_RULE
let param = this.rule.param.substring(this.rule.param.indexOf("${") + 2, this.rule.param.indexOf("}"))
let index = param.indexOf(".")
if (index > 0) {
this.rule.checkpointPrefix = param.substring(0, index)
this.rule.checkpointParam = param.substring(index + 1)
} else {
this.rule.checkpointPrefix = param
}
this.$delay(function () {
this.changeCheckpoint()
if (this.rule.checkpointOptions != null && this.checkpoint != null && this.checkpoint.options != null) {
let that = this
this.checkpoint.options.forEach(function (option) {
if (typeof (that.rule.checkpointOptions[option.code]) != "undefined") {
option.value = that.rule.checkpointOptions[option.code]
}
})
}
})
}
/**
* checkpoint
*/
this.checkpoint = null
this.changeCheckpoint = function () {
if (this.rule.checkpointPrefix.length == 0) {
this.checkpoint = null
return
}
let that = this
this.checkpoint = this.checkpoints.$find(function (k, v) {
return v.prefix == that.rule.checkpointPrefix
})
}
/**
* operator
*/
this.changeOperator = function () {
let that = this;
this.operator = this.operators.$find(function (k, v) {
return v.code == that.rule.operator
})
if (!this.isUpdating) {
this.rule.isCaseInsensitive = (this.operator.case == "yes")
}
};
this.changeOperator()
})

View File

@@ -0,0 +1,67 @@
{$layout "layout_popup"}
<h3>添加规则集</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="groupId" :value="groupId"/>
<table class="ui table definition selectable">
<tr>
<td class="title">规则集名称 *</td>
<td>
<input type="text" name="name" maxlength="100" ref="focus"/>
</td>
</tr>
<tr>
<td>规则</td>
<td>
<http-firewall-rules-box :v-rules="rules" :v-type="type"></http-firewall-rules-box>
</td>
</tr>
<tr>
<td>规则之间的关系</td>
<td>
<select class="ui dropdown" name="connector" style="width:10em" @change="changeConnector()" v-model="selectedConnector">
<option v-for="connector in connectors" :value="connector.value">{{connector.name}}</option>
</select>
<p class="comment">{{selectedConnectorDescription}}</p>
</td>
</tr>
<tr>
<td :class="{'color-border':action == 'go_group' || action == 'go_set'}">动作</td>
<td>
<select class="ui dropdown auto-width" name="action" v-model="action">
<option v-for="a in actions" :value="a.code">{{a.name}}({{a.code.toUpperCase()}})</option>
</select>
<p class="comment">匹配当前规则集后要执行的动作。</p>
</td>
</tr>
<tr v-if="action == 'go_group'">
<td class="color-border">下一个规则分组</td>
<td>
<select class="ui dropdown" name="action_groupId" style="width:12em" v-model="actionGroupId">
<option v-for="g in firewallPolicy.inbound.groups" :value="g.id">{{g.name}}</option>
</select>
<p class="comment">当动作为"跳到下一个规则分组"时出现此选择。</p>
</td>
</tr>
<tr v-if="action == 'go_set'">
<td class="color-border">下一个规则分组</td>
<td>
<select class="ui dropdown" name="action_groupId" style="width:12em" v-model="actionGroupId">
<option v-for="g in firewallPolicy.inbound.groups" :value="g.id">{{g.name}}</option>
</select>
<p class="comment">当动作为"跳到下一个规则集"时出现此选择。</p>
</td>
</tr>
<tr v-if="action == 'go_set'">
<td class="color-border">下一个规则集</td>
<td>
<select class="ui dropdown" name="action_setId" style="width:12em" v-model="actionSetId">
<option v-for="r in groupSets(actionGroupId)" :value="r.id">{{r.name}}</option>
</select>
<p class="comment">当动作为"跳到下一个规则集"时出现此选择。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,43 @@
Tea.context(function () {
this.success = NotifyPopup
// rules
this.rules = []
// connector
this.selectedConnector = this.connectors[1].value
this.selectedConnectorDescription = ""
this.changeConnector = function () {
let that = this
this.selectedConnectorDescription = this.connectors.$find(function (k, v) {
return v.value == that.selectedConnector
}).description
}
this.changeConnector()
// action
this.action = "block"
// action:go_group
this.actionGroupId = 0
// action:go_set
this.actionSetId = 0
this.groupSets = function (groupId) {
let group = null
this.firewallPolicy.inbound.groups.forEach(function (v) {
if (v.id == groupId) {
group = v
}
})
this.firewallPolicy.outbound.groups.forEach(function (v) {
if (v.id == groupId) {
group = v
}
})
if (group == null) {
return []
}
return group.sets
}
})

View File

@@ -0,0 +1,69 @@
{$layout}
{$template "/left_menu"}
<div class="right-box">
{$template "waf_menu"}
<h3>分组<a href="" @click.prevent="updateGroup(group.id)">[修改]</a></h3>
<table class="ui table selectable definition">
<tr>
<td class="title">名称</td>
<td>{{group.name}}</td>
</tr>
<tr>
<td>描述</td>
<td>
<span v-if="group.description.length == 0" class="disabled">暂时还没有描述。</span>
<span v-if="group.description.length > 0">{{group.description}}</span>
</td>
</tr>
<tr>
<td>启用状态</td>
<td>
<label-on :v-is-on="group.isOn"></label-on>
</td>
</tr>
</table>
<h3 style="padding-top:0.8em">规则集<a href="" @click.prevent="createSet(group.id)">[添加规则集]</a> </h3>
<p class="comment" v-if="sets == null || sets.length == 0">暂时还没有规则。</p>
<table class="ui table selectable" id="sortable-table" v-if="sets != null && sets.length > 0">
<thead>
<tr>
<th style="width:3em"></th>
<th nowrap="">规则集名称</th>
<th nowrap="">规则</th>
<th nowrap="">关系</th>
<th nowrap="">动作</th>
<th class="three op">操作</th>
</tr>
</thead>
<tbody v-for="set in sets" :data-set-id="set.id">
<tr>
<td style="text-align: center;"><i class="icon bars handle grey"></i> </td>
<td><span :class="{disabled:!set.isOn}">{{set.name}}</span>
<p style="margin-top:0.5em">
<label-on :v-is-on="set.isOn"></label-on>
</p>
</td>
<td class="rules-box">
<div v-for="rule in set.rules" style="margin-top: 0.4em;margin-bottom:0.4em">
<span class="ui label tiny">{{rule.name}}[{{rule.param}}] <var :class="{dash:rule.isCaseInsensitive}" :title="rule.isCaseInsensitive ? '大小写不敏感':''">{{rule.operator}}</var> {{rule.value}}</span>
</div>
<span class="ui disabled" v-if="set.rules.length == 0">暂时还没有规则</span>
</td>
<td>{{set.connector.toUpperCase()}}</td>
<td><span :class="{red:set.action == 'BLOCK' || set.action == 'CAPTCHA', green:set.action != 'BLOCK' && set.action != 'CAPTCHA'}">{{set.actionName}}[{{set.action.toUpperCase()}}]</span>
<div v-if="set.actionLinks != null && set.actionLinks.length > 0" style="margin-top:0.3em">
<span class="disabled">-&gt;</span> <span v-for="link in set.actionLinks"><a :href="link.url"><span class="disabled">[{{link.name}}]</span></a> &nbsp;</span>
</div>
</td>
<td>
<a href="" @click.prevent="updateSet(set.id)">修改</a> &nbsp; <a href="" @click.prevent="updateSetOn(set.id, false)" v-if="set.isOn">停用</a><a href="" @click.prevent="updateSetOn(set.id, true)" v-if="!set.isOn">启用</a> &nbsp; <a href="" @click.prevent="deleteSet(set.id)">删除</a>
</td>
</tr>
</tbody>
</table>
<p class="comment" v-if="group.sets != null && group.sets.length > 1">所有规则匹配顺序为从上到下,可以拖动左侧的<i class="icon bars"></i>排序。</p>
</div>

View File

@@ -0,0 +1,82 @@
Tea.context(function () {
this.$delay(function () {
let that = this
sortTable(function () {
let setIds = []
document
.querySelectorAll("tbody[data-set-id]")
.forEach(function (v) {
setIds.push(v.getAttribute("data-set-id"))
})
that.$post(".sortSets")
.params({
groupId: that.group.id,
setIds: setIds
})
.success(function () {
teaweb.successToast("排序保存成功")
})
})
})
// 更改分组
this.updateGroup = function (groupId) {
teaweb.popup("/servers/components/waf/updateGroupPopup?groupId=" + groupId, {
height: "16em",
callback: function () {
teaweb.success("保存成功", function () {
window.location.reload()
})
}
})
}
// 创建规则集
this.createSet = function (groupId) {
teaweb.popup("/servers/components/waf/createSetPopup?firewallPolicyId=" + this.firewallPolicyId + "&groupId=" + groupId + "&type=" + this.type, {
width: "50em",
height: "30em",
callback: function () {
teaweb.success("保存成功", function () {
window.location.reload()
})
}
})
}
// 修改规则集
this.updateSet = function (setId) {
teaweb.popup("/servers/components/waf/updateSetPopup?firewallPolicyId=" + this.firewallPolicyId + "&groupId=" + this.group.id + "&type=" + this.type + "&setId=" + setId, {
width: "50em",
height: "30em",
callback: function () {
teaweb.success("保存成功", function () {
window.location.reload()
})
}
})
}
// 停用|启用规则集
this.updateSetOn = function (setId, isOn) {
this.$post(".updateSetOn")
.params({
setId: setId,
isOn: isOn ? 1 : 0
})
.refresh()
}
// 删除规则集
this.deleteSet = function (setId) {
let that = this
teaweb.confirm("确定要删除此规则集吗?", function () {
that.$post(".deleteSet")
.params({
groupId: this.group.id,
setId: setId
})
.refresh()
})
}
})

View File

@@ -32,7 +32,7 @@
</p>
</td>
<td>
<a :href="'/proxy/components/waf/sets?firewallPolicyId=' + firewallPolicyId + '&type=' + type + '&groupId=' + group.id">{{group.countSets}}</a>
<a :href="'/servers/components/waf/group?firewallPolicyId=' + firewallPolicyId + '&type=' + type + '&groupId=' + group.id">{{group.countSets}}</a>
</td>
<td>
<a :href="'/servers/components/waf/group?firewallPolicyId=' + firewallPolicyId + '&type=' + type + '&groupId=' + group.id">详情</a> &nbsp;
@@ -42,4 +42,6 @@
</tr>
</tbody>
</table>
<p class="comment" v-if="groups.length > 0">所有规则匹配顺序为从上到下,可以拖动左侧的<i class="icon bars"></i>排序。</p>
</div>

View File

@@ -0,0 +1,38 @@
{$layout "layout_popup"}
<h3>修改分组</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="groupId" :value="group.id"/>
<table class="ui table definition selectable">
<tr>
<td class="title">分组名称 *</td>
<td>
<input type="text" name="name" maxlength="100" ref="focus" v-model="group.name"/>
<p class="comment">给分组起一个容易识别的名称</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>分组描述</td>
<td>
<textarea name="description" maxlength="200" rows="3" v-model="group.description"></textarea>
</td>
</tr>
<tr>
<td>是否启用</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="isOn" value="1" v-model="group.isOn" />
<label></label>
</div>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyPopup
})

View File

@@ -0,0 +1,67 @@
{$layout "layout_popup"}
<h3>修改规则集</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="setId" :value="setConfig.id"/>
<table class="ui table definition selectable">
<tr>
<td class="title">规则集名称 *</td>
<td>
<input type="text" name="name" maxlength="100" ref="focus" v-model="setConfig.name"/>
</td>
</tr>
<tr>
<td>规则</td>
<td>
<http-firewall-rules-box :v-rules="rules" :v-type="type"></http-firewall-rules-box>
</td>
</tr>
<tr>
<td>规则之间的关系</td>
<td>
<select class="ui dropdown" name="connector" style="width:10em" @change="changeConnector()" v-model="selectedConnector">
<option v-for="connector in connectors" :value="connector.value">{{connector.name}}</option>
</select>
<p class="comment">{{selectedConnectorDescription}}</p>
</td>
</tr>
<tr>
<td :class="{'color-border':action == 'go_group' || action == 'go_set'}">动作</td>
<td>
<select class="ui dropdown auto-width" name="action" v-model="action">
<option v-for="a in actions" :value="a.code">{{a.name}}({{a.code.toUpperCase()}})</option>
</select>
<p class="comment">匹配当前规则集后要执行的动作。</p>
</td>
</tr>
<tr v-if="action == 'go_group'">
<td class="color-border">下一个规则分组</td>
<td>
<select class="ui dropdown" name="action_groupId" style="width:12em" v-model="actionGroupId">
<option v-for="g in firewallPolicy.inbound.groups" :value="g.id">{{g.name}}</option>
</select>
<p class="comment">当动作为"跳到下一个规则分组"时出现此选择。</p>
</td>
</tr>
<tr v-if="action == 'go_set'">
<td class="color-border">下一个规则分组</td>
<td>
<select class="ui dropdown" name="action_groupId" style="width:12em" v-model="actionGroupId">
<option v-for="g in firewallPolicy.inbound.groups" :value="g.id">{{g.name}}</option>
</select>
<p class="comment">当动作为"跳到下一个规则集"时出现此选择。</p>
</td>
</tr>
<tr v-if="action == 'go_set'">
<td class="color-border">下一个规则集</td>
<td>
<select class="ui dropdown" name="action_setId" style="width:12em" v-model="actionSetId">
<option v-for="r in groupSets(actionGroupId)" :value="r.id">{{r.name}}</option>
</select>
<p class="comment">当动作为"跳到下一个规则集"时出现此选择。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,57 @@
Tea.context(function () {
this.success = NotifyPopup
// rules
this.rules = this.setConfig.rules
// connector
this.selectedConnector = this.setConfig.connector
this.selectedConnectorDescription = ""
this.changeConnector = function () {
let that = this
this.selectedConnectorDescription = this.connectors.$find(function (k, v) {
return v.value == that.selectedConnector
}).description
}
this.changeConnector()
// action
this.action = this.setConfig.action
// action:go_group
this.actionGroupId = 0
if (this.action == "go_group" || this.action == "go_set" && this.setConfig.actionOptions != null) {
this.$delay(function () {
this.actionGroupId = this.setConfig.actionOptions["groupId"]
})
}
// action:go_set
this.actionSetId = 0
if (this.action == "go_set" && this.setConfig.actionOptions != null) {
this.$delay(function () {
this.actionSetId = this.setConfig.actionOptions["setId"]
})
}
this.groupSets = function (groupId) {
if (this.firewallPolicy == null) {
return
}
let group = null
this.firewallPolicy.inbound.groups.forEach(function (v) {
if (v.id == groupId) {
group = v
}
})
this.firewallPolicy.outbound.groups.forEach(function (v) {
if (v.id == groupId) {
group = v
}
})
if (group == null) {
return []
}
return group.sets
}
})