This is an automated email from the ASF dual-hosted git repository.

mark4z pushed a commit to branch 0.4.0
in repository https://gitbox.apache.org/repos/asf/dubbo-go-pixiu.git

commit 70bdd3eeb4029a633ea126420a2bd750681a8ecd
Author: baerwang <[email protected]>
AuthorDate: Sun Oct 17 19:52:18 2021 +0800

    add:csrf
    
    Former-commit-id: 154ad17d311c624e4c2c6a4bee4c16337197545a
---
 pkg/common/constant/key.go                     |   1 +
 pkg/filter/csrf/csrf.go                        | 141 +++++++++++++++++++++++++
 pkg/pluginregistry/registry.go                 |   1 +
 samples/dubbogo/simple/csrf/pixiu/conf.yaml    |  78 ++++++++++++++
 samples/dubbogo/simple/csrf/server/server.go   |  31 ++++++
 samples/dubbogo/simple/csrf/test/pixiu_test.go |  58 ++++++++++
 6 files changed, 310 insertions(+)

diff --git a/pkg/common/constant/key.go b/pkg/common/constant/key.go
index d51563d..f3335e7 100644
--- a/pkg/common/constant/key.go
+++ b/pkg/common/constant/key.go
@@ -35,6 +35,7 @@ const (
        HTTPTimeoutFilter      = "dgp.filter.http.timeout"
        TracingFilter          = "dgp.filters.tracing"
        HTTPCorsFilter         = "dgp.filter.http.cors"
+       HTTPCsrfFilter         = "dgp.filter.http.csrf"
        HTTPProxyRewriteFilter = "dgp.filter.http.proxyrewrite"
 )
 
diff --git a/pkg/filter/csrf/csrf.go b/pkg/filter/csrf/csrf.go
new file mode 100644
index 0000000..ba85f08
--- /dev/null
+++ b/pkg/filter/csrf/csrf.go
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 csrf
+
+import (
+       "encoding/base64"
+       "encoding/json"
+       "fmt"
+       "github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+       "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+       "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+       http2 "net/http"
+)
+
+const (
+       // Kind is the kind of Fallback.
+       Kind = constant.HTTPCsrfFilter
+)
+
+const (
+       csrfSecret = "csrfSecret"
+       csrfSalt   = "csrfSalt"
+)
+
+func init() {
+       filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+       // Plugin is http filter plugin.
+       Plugin struct {
+       }
+
+       // Filter is http filter instance
+       Filter struct {
+               cfg *Config
+       }
+
+       // Config describe the config of Filter
+       Config struct {
+               Key           string   `yaml:"key" json:"key" 
mapstructure:"key"`                                  // get request key
+               Secret        string   `yaml:"secret" json:"secret" 
mapstructure:"secret"`                         // private key
+               ErrorMsg      string   `yaml:"error_msg" json:"error_msg" 
mapstructure:"error_msg"`                // hint error info
+               IgnoreMethods []string `yaml:"ignore_methods" 
json:"ignore_methods" mapstructure:"ignore_methods"` // ignore request method
+       }
+)
+
+func (p *Plugin) Kind() string {
+       return Kind
+}
+
+func (p *Plugin) CreateFilter() (filter.HttpFilter, error) {
+       return &Filter{cfg: &Config{}}, nil
+}
+
+func (f *Filter) PrepareFilterChain(ctx *http.HttpContext) error {
+       ctx.AppendFilterFunc(f.Handle)
+       return nil
+}
+
+func (f *Filter) Handle(ctx *http.HttpContext) {
+       f.handleCsrf(ctx)
+}
+
+func (f *Filter) handleCsrf(ctx *http.HttpContext) {
+       ctx.Request.Header.Set(csrfSecret, f.cfg.Secret)
+
+       if inMethod(f.cfg.IgnoreMethods, ctx.Request.Method) {
+               ctx.Next()
+               return
+       }
+
+       salt := ctx.Request.Header.Get(csrfSalt)
+
+       if salt == "" {
+               bt, _ := json.Marshal(http.ErrResponse{Message: f.cfg.ErrorMsg})
+               ctx.WriteJSONWithStatus(http2.StatusForbidden, bt)
+               ctx.Abort()
+               return
+       }
+
+       token := tokenize(f.cfg.Secret, salt)
+
+       if token != tokenGetter(ctx, f.cfg.Key) {
+               bt, _ := json.Marshal(http.ErrResponse{Message: f.cfg.ErrorMsg})
+               ctx.WriteJSONWithStatus(http2.StatusForbidden, bt)
+               ctx.Abort()
+               return
+       }
+
+       ctx.Next()
+
+}
+
+func tokenGetter(ctx *http.HttpContext, key string) string {
+       req := ctx.Request
+       if t := req.Form.Get(key); t != "" {
+               return t
+       } else if t := req.URL.Query().Get(key); t != "" {
+               return t
+       } else if t := req.Header.Get(key); t != "" {
+               return t
+       }
+       return ""
+}
+
+func inMethod(methods []string, method string) bool {
+       for _, v := range methods {
+               if v == method {
+                       return true
+               }
+       }
+       return false
+}
+
+func tokenize(secret, salt string) string {
+       return base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf("%s-%s", 
salt, secret)))
+}
+
+func (f *Filter) Apply() error {
+       return nil
+}
+
+func (f *Filter) Config() interface{} {
+       return f.cfg
+}
diff --git a/pkg/pluginregistry/registry.go b/pkg/pluginregistry/registry.go
index 7c97bb2..027619d 100644
--- a/pkg/pluginregistry/registry.go
+++ b/pkg/pluginregistry/registry.go
@@ -24,6 +24,7 @@ import (
        _ "github.com/apache/dubbo-go-pixiu/pkg/filter/accesslog"
        _ "github.com/apache/dubbo-go-pixiu/pkg/filter/authority"
        _ "github.com/apache/dubbo-go-pixiu/pkg/filter/cors"
+       _ "github.com/apache/dubbo-go-pixiu/pkg/filter/csrf"
        _ "github.com/apache/dubbo-go-pixiu/pkg/filter/header"
        _ "github.com/apache/dubbo-go-pixiu/pkg/filter/host"
        _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/apiconfig"
diff --git a/samples/dubbogo/simple/csrf/pixiu/conf.yaml 
b/samples/dubbogo/simple/csrf/pixiu/conf.yaml
new file mode 100644
index 0000000..093f9ce
--- /dev/null
+++ b/samples/dubbogo/simple/csrf/pixiu/conf.yaml
@@ -0,0 +1,78 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+#
+---
+static_resources:
+  listeners:
+    - name: "net/http"
+      address:
+        socket_address:
+          protocol_type: "HTTP"
+          address: "0.0.0.0"
+          port: 8888
+      filter_chains:
+        - filter_chain_match:
+          domains:
+            - api.dubbo.com
+            - api.pixiu.com
+          filters:
+            - name: dgp.filter.httpconnectionmanager
+              config:
+                route_config:
+                  routes:
+                    - match:
+                        prefix: "/user"
+                      route:
+                        cluster: "user"
+                        cluster_not_found_response_code: 505
+                http_filters:
+                  - name: dgp.filter.http.httpproxy
+                    config:
+                  - name: dgp.filter.http.cors
+                    config:
+                      allow_origin:
+                        - api.dubbo.com
+                      allow_methods: ""
+                      allow_headers: ""
+                      expose_headers: ""
+                      max_age: ""
+                      allow_credentials: false
+                  - name: dgp.filter.http.csrf
+                    config:
+                      key: pixiu
+                      secret: pixiu888
+                      ignore_methods:
+                      error_msg: "CSRF token mismatch"
+                  - name: dgp.filter.http.response
+                    config:
+      config:
+        idle_timeout: 5s
+        read_timeout: 5s
+        write_timeout: 5s
+  clusters:
+    - name: "user"
+      lb_policy: "lb"
+      endpoints:
+        - id: 1
+          socket_address:
+            address: 127.0.0.1
+            port: 1314
+  shutdown_config:
+    timeout: "60s"
+    step_timeout: "10s"
+    reject_policy: "immediacy"
\ No newline at end of file
diff --git a/samples/dubbogo/simple/csrf/server/server.go 
b/samples/dubbogo/simple/csrf/server/server.go
new file mode 100644
index 0000000..6eccca7
--- /dev/null
+++ b/samples/dubbogo/simple/csrf/server/server.go
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 main
+
+import (
+       "log"
+       "net/http"
+)
+
+func main() {
+       http.HandleFunc("/user/", func(w http.ResponseWriter, r *http.Request) {
+               _, _ = w.Write([]byte(`{"message":"success","status":200}`))
+       })
+       log.Println("Starting sample server ...")
+       log.Fatal(http.ListenAndServe(":1314", nil))
+}
diff --git a/samples/dubbogo/simple/csrf/test/pixiu_test.go 
b/samples/dubbogo/simple/csrf/test/pixiu_test.go
new file mode 100644
index 0000000..2fed5cf
--- /dev/null
+++ b/samples/dubbogo/simple/csrf/test/pixiu_test.go
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 csrf
+
+import (
+       "github.com/stretchr/testify/assert"
+       "io/ioutil"
+       "net/http"
+       "strings"
+       "testing"
+       "time"
+)
+
+func TestCsrfHeader(t *testing.T) {
+       urlStr := "http://localhost:8888/user/";
+       client := &http.Client{Timeout: 5 * time.Second}
+       req, err := http.NewRequest("GET", urlStr, nil)
+       assert.NoError(t, err)
+       req.Header.Set("csrfSalt", "pixiu")
+       req.Header.Set("pixiu", "cGl4aXUtcGl4aXU4ODg=")
+       resp, err := client.Do(req)
+       assert.NoError(t, err)
+       assert.Equal(t, http.StatusOK, resp.StatusCode)
+       assert.NotNil(t, resp)
+       s, _ := ioutil.ReadAll(resp.Body)
+       t.Log(string(s))
+       assert.True(t, strings.Contains(string(s), "success"))
+}
+
+func TestCsrfQuery(t *testing.T) {
+       urlStr := "http://localhost:8888/user?pixiu=cGl4aXUtcGl4aXU4ODg=";
+       client := &http.Client{Timeout: 5 * time.Second}
+       req, err := http.NewRequest("GET", urlStr, nil)
+       assert.NoError(t, err)
+       req.Header.Set("csrfSalt", "pixiu")
+       resp, err := client.Do(req)
+       assert.NoError(t, err)
+       assert.Equal(t, http.StatusOK, resp.StatusCode)
+       assert.NotNil(t, resp)
+       s, _ := ioutil.ReadAll(resp.Body)
+       t.Log(string(s))
+       assert.True(t, strings.Contains(string(s), "success"))
+}

Reply via email to