Skip to content

Commit

Permalink
feat: add action object (#73)
Browse files Browse the repository at this point in the history
* feat: support new action in proxy.go

* feat: support rule action in the frontend

* chore: rename ActionId into Id and update empty action type into allow
  • Loading branch information
love98ooo authored Sep 15, 2024
1 parent a4b42f3 commit 4d353b4
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 54 deletions.
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func main() {
proxy.InitHttpClient()
object.InitSiteMap()
object.InitRuleMap()
object.InitActionMap()
run.InitAppMap()
run.InitSelfStart()
object.StartMonitorSitesLoop()
Expand Down
10 changes: 9 additions & 1 deletion object/action_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func refreshActionMap() error {
return nil
}

func GetActionsByActionIds(ids []string) ([]*Action, error) {
func GetActionsByIds(ids []string) ([]*Action, error) {
var res []*Action
for _, id := range ids {
action, ok := actionMap[id]
Expand All @@ -55,3 +55,11 @@ func GetActionsByActionIds(ids []string) ([]*Action, error) {
}
return res, nil
}

func GetActionById(id string) (*Action, error) {
action, ok := actionMap[id]
if !ok {
return nil, fmt.Errorf("action: %s not found", id)
}
return action, nil
}
45 changes: 34 additions & 11 deletions rule/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ type Rule interface {
checkRule(expressions []*object.Expression, req *http.Request) (bool, string, string, error)
}

func CheckRules(ruleIds []string, r *http.Request) (string, string, error) {
func CheckRules(ruleIds []string, r *http.Request) (*object.Action, string, error) {
var actionObj *object.Action
rules, err := object.GetRulesByRuleIds(ruleIds)
if err != nil {
return "", "", err
return nil, "", err
}
for i, rule := range rules {
var ruleObj Rule
Expand All @@ -46,15 +47,36 @@ func CheckRules(ruleIds []string, r *http.Request) (string, string, error) {
case "Compound":
ruleObj = &CompoundRule{}
default:
return "", "", fmt.Errorf("unknown rule type: %s for rule: %s", rule.Type, rule.GetId())
return nil, "", fmt.Errorf("unknown rule type: %s for rule: %s", rule.Type, rule.GetId())
}

isHit, action, reason, err := ruleObj.checkRule(rule.Expressions, r)
if err != nil {
return "", "", err
return nil, "", err
}
if action == "" {
action = rule.Action
actionObj, err = object.GetActionById(rule.Action)
if err != nil {
return nil, "", err
}
action = actionObj.Type
} else {
switch action {
case "Block":
actionObj.Type = "Block"
actionObj.StatusCode = 403
case "Drop":
actionObj.Type = "Drop"
actionObj.StatusCode = 400
case "Allow":
actionObj.Type = "Allow"
actionObj.StatusCode = 200
case "Captcha":
actionObj.Type = "Captcha"
actionObj.StatusCode = 302
default:
return nil, "", fmt.Errorf("unknown rule action: %s for rule: %s", action, rule.GetId())
}
}
if isHit {
if action == "Block" || action == "Drop" {
Expand All @@ -63,16 +85,17 @@ func CheckRules(ruleIds []string, r *http.Request) (string, string, error) {
} else {
reason = fmt.Sprintf("hit rule %s: %s", ruleIds[i], reason)
}
return action, reason, nil
return actionObj, reason, nil
} else if action == "Allow" {
return action, reason, nil
return actionObj, reason, nil
} else if action == "Captcha" {
return action, reason, nil
return actionObj, reason, nil
} else {
return "", "", fmt.Errorf("unknown rule action: %s for rule: %s", action, rule.GetId())
return nil, "", fmt.Errorf("unknown rule action: %s for rule: %s", action, rule.GetId())
}
}
}

return "", "", nil
actionObj.Type = "Allow"
actionObj.StatusCode = 200
return actionObj, "", nil
}
2 changes: 1 addition & 1 deletion rule/rule_compound.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (r *CompoundRule) checkRule(expressions []*object.Expression, req *http.Req
if err != nil {
return false, "", "", err
}
if action == "" {
if action.Type == "" {
isHit = false
}
switch expression.Operator {
Expand Down
8 changes: 4 additions & 4 deletions service/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,15 +205,15 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
reason = "the rule has been hit"
}

switch action {
switch action.Type {
case "", "Allow":
w.WriteHeader(http.StatusOK)
w.WriteHeader(action.StatusCode)
case "Block":
responseError(w, "Blocked by CasWAF: %s", reason)
w.WriteHeader(http.StatusForbidden)
w.WriteHeader(action.StatusCode)
case "Drop":
responseError(w, "Dropped by CasWAF: %s", reason)
w.WriteHeader(http.StatusBadRequest)
w.WriteHeader(action.StatusCode)
case "Captcha":
ok := isVerifiedSession(r)
if ok {
Expand Down
65 changes: 28 additions & 37 deletions web/src/RuleEditPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
// limitations under the License.

import React from "react";
import {Button, Card, Col, Input, InputNumber, Row, Select} from "antd";
import {Button, Card, Col, Input, Row, Select} from "antd";
import * as Setting from "./Setting";
import * as RuleBackend from "./backend/RuleBackend";
import * as ActionBackend from "./backend/ActionBackend";
import i18next from "i18next";
import WafRuleTable from "./components/WafRuleTable";
import IpRuleTable from "./components/IpRuleTable";
Expand All @@ -33,11 +34,13 @@ class RuleEditPage extends React.Component {
owner: props.match.params.owner,
ruleName: props.match.params.ruleName,
rule: null,
actions: [],
};
}

UNSAFE_componentWillMount() {
this.getRule();
this.getActions();
}

getRule() {
Expand All @@ -48,6 +51,14 @@ class RuleEditPage extends React.Component {
});
}

getActions() {
ActionBackend.getActions(this.state.owner).then((res) => {
this.setState({
actions: res.data,
});
});
}

updateRuleField(key, value) {
const rule = Setting.deepCopy(this.state.rule);
rule[key] = value;
Expand Down Expand Up @@ -172,52 +183,32 @@ class RuleEditPage extends React.Component {
{i18next.t("general:Action")}:
</Col>
<Col span={22}>
<Select virtual={false} value={this.state.rule.action} defaultValue={"Block"} style={{width: "100%"}} onChange={value => {
<Select virtual={false} value={this.state.rule.action} defaultValue={"Block"} style={{width: "100%"}} onChange={(value, index) => {
this.setState({
action: this.state.actions[index],
});
this.updateRuleField("action", value);
}}>
{
[
{value: "Allow", text: i18next.t("rule:Allow")},
// {value: "redirect", text: "Redirect"},
{value: "Block", text: i18next.t("rule:Block")},
// {value: "drop", text: "Drop"},
{value: "Captcha", text: i18next.t("rule:Captcha")},
].map((item, index) => <Option key={index} value={item.value}>{item.text}</Option>)
this.state.actions.map((action, index) => <Option key={index} value={action.owner + "/" + action.name} label={action.type}></Option>)
}
</Select>
</Col>
</Row>
)
}
{
(this.state.rule.action === "Block" && this.state.rule.type !== "WAF") && (
<Row style={{marginTop: "20px"}}>
<Col span={2} style={{marginTop: "5px"}}>
{i18next.t("rule:Status Code")}:
</Col>
<Col span={22}>
<InputNumber value={this.state.rule.statusCode} defaultValue={403} disabled={true}
onChange={e => {
this.updateRuleField("statusCode", e.target.value);
}} />
</Col>
</Row>
)
}
{
(this.state.rule.action === "Block" || this.state.rule.type === "WAF") && (
<Row style={{marginTop: "20px"}}>
<Col span={2} style={{marginTop: "5px"}}>
{i18next.t("rule:Reason")}:
</Col>
<Col span={22}>
<Input value={this.state.rule.reason}
onChange={e => {
this.updateRuleField("reason", e.target.value);
}} />
</Col>
</Row>
)
<Row style={{marginTop: "20px"}}>
<Col span={2} style={{marginTop: "5px"}}>
{i18next.t("rule:Reason")}:
</Col>
<Col span={22}>
<Input value={this.state.rule.reason}
onChange={e => {
this.updateRuleField("reason", e.target.value);
}} />
</Col>
</Row>
}
</Card>
);
Expand Down

0 comments on commit 4d353b4

Please sign in to comment.