This is an automated email from the ASF dual-hosted git repository. hsluoyz pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/casbin-gateway.git
commit 3561e266d8abed574b070058d5f5a698daa51416 Author: Yang Luo <[email protected]> AuthorDate: Sun Mar 29 21:23:34 2026 +0800 feat: add UrlPathRule --- rule/rule.go | 2 ++ rule/rule_url_path.go | 62 +++++++++++++++++++++++++++++++++++++++ web/src/RuleEditPage.js | 13 ++++++++ web/src/components/UaRuleTable.js | 39 ++++++++++++++++++------ 4 files changed, 107 insertions(+), 9 deletions(-) diff --git a/rule/rule.go b/rule/rule.go index 0468c3f..4c689da 100644 --- a/rule/rule.go +++ b/rule/rule.go @@ -42,6 +42,8 @@ func CheckRules(ruleIds []string, r *http.Request) (*RuleResult, error) { switch rule.Type { case "User-Agent": ruleObj = &UaRule{} + case "URL Path": + ruleObj = &UrlPathRule{} case "IP": ruleObj = &IpRule{} case "WAF": diff --git a/rule/rule_url_path.go b/rule/rule_url_path.go new file mode 100644 index 0000000..93ef412 --- /dev/null +++ b/rule/rule_url_path.go @@ -0,0 +1,62 @@ +// Copyright 2024 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rule + +import ( + "fmt" + "net/http" + "regexp" + "strings" + + "github.com/casbin/caswaf/object" +) + +type UrlPathRule struct{} + +func (r *UrlPathRule) checkRule(expressions []*object.Expression, req *http.Request) (*RuleResult, error) { + path := req.URL.Path + for _, expression := range expressions { + pattern := expression.Value + reason := fmt.Sprintf("expression matched: \"%s %s %s\"", path, expression.Operator, expression.Value) + switch expression.Operator { + case "contains": + if strings.Contains(path, pattern) { + return &RuleResult{Reason: reason}, nil + } + case "does not contain": + if !strings.Contains(path, pattern) { + return &RuleResult{Reason: reason}, nil + } + case "equals": + if path == pattern { + return &RuleResult{Reason: reason}, nil + } + case "does not equal": + if strings.Compare(path, pattern) != 0 { + return &RuleResult{Reason: reason}, nil + } + case "match": + isHit, err := regexp.MatchString(pattern, path) + if err != nil { + return nil, err + } + if isHit { + return &RuleResult{Reason: reason}, nil + } + } + } + + return nil, nil +} diff --git a/web/src/RuleEditPage.js b/web/src/RuleEditPage.js index 57c7b60..65b1e47 100644 --- a/web/src/RuleEditPage.js +++ b/web/src/RuleEditPage.js @@ -99,6 +99,7 @@ class RuleEditPage extends React.Component { {value: "WAF", text: "WAF"}, {value: "IP", text: "IP"}, {value: "User-Agent", text: "User-Agent"}, + {value: "URL Path", text: "URL Path"}, {value: "IP Rate Limiting", text: i18next.t("rule:IP Rate Limiting")}, {value: "Compound", text: i18next.t("rule:Compound")}, ].map((item, index) => <Option key={index} value={item.value}>{item.text}</Option>) @@ -144,6 +145,18 @@ class RuleEditPage extends React.Component { /> ) : null } + { + this.state.rule.type === "URL Path" ? ( + <UaRuleTable + kind="urlPath" + title={"URL Paths"} + table={this.state.rule.expressions} + ruleName={this.state.rule.name} + account={this.props.account} + onUpdateTable={(value) => {this.updateRuleField("expressions", value);}} + /> + ) : null + } { this.state.rule.type === "IP Rate Limiting" ? ( <IpRateRuleTable diff --git a/web/src/components/UaRuleTable.js b/web/src/components/UaRuleTable.js index 3d4e92d..c5fe6ef 100644 --- a/web/src/components/UaRuleTable.js +++ b/web/src/components/UaRuleTable.js @@ -25,19 +25,31 @@ class UaRuleTable extends React.Component { super(props); this.state = { classes: props, - defaultRules: [ - { - name: "Current User-Agent", - operator: "equals", - value: window.navigator.userAgent, - }, - ], }; if (this.props.table.length === 0) { this.restore(); } } + getDefaultRules() { + if (this.props.kind === "urlPath") { + return [ + { + name: "Example", + operator: "contains", + value: "/.git/config", + }, + ]; + } + return [ + { + name: "Current User-Agent", + operator: "equals", + value: window.navigator.userAgent, + }, + ]; + } + updateTable(table) { this.props.onUpdateTable(table); } @@ -48,7 +60,12 @@ class UaRuleTable extends React.Component { } addRow(table) { - const row = {name: `New UA Rule - ${table.length}`, operator: "equals", value: ""}; + const isPath = this.props.kind === "urlPath"; + const row = { + name: isPath ? `New URL Path Rule - ${table.length}` : `New UA Rule - ${table.length}`, + operator: isPath ? "contains" : "equals", + value: "", + }; if (table === undefined) { table = []; } @@ -73,7 +90,7 @@ class UaRuleTable extends React.Component { } restore() { - this.updateTable(this.state.defaultRules); + this.updateTable(this.getDefaultRules()); } renderTable(table) { @@ -169,4 +186,8 @@ class UaRuleTable extends React.Component { } } +UaRuleTable.defaultProps = { + kind: "userAgent", +}; + export default UaRuleTable; --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
