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


The following commit(s) were added to refs/heads/master by this push:
     new 09f2400  feat: add UrlPathRule
09f2400 is described below

commit 09f2400d9bc822a67fcab220689bf870da4691a7
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]

Reply via email to