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

chenjunxu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 678b94d  fix: error check to ensure path id doesn't conflict body id 
(#1067)
678b94d is described below

commit 678b94d643c4f0bbbbcebbfe51025ada768b6165
Author: Jinchi Zhou <[email protected]>
AuthorDate: Fri Dec 25 22:58:34 2020 -0800

    fix: error check to ensure path id doesn't conflict body id (#1067)
    
    Co-authored-by: nic-chen <[email protected]>
---
 api/internal/core/store/store.go               |  1 -
 api/internal/handler/handler.go                | 21 +++++-
 api/internal/handler/handler_test.go           | 86 ++++++++++++++++++++++++
 api/internal/handler/route/route.go            | 12 ++--
 api/internal/handler/route/route_test.go       | 90 +++++++++++++++++++++++++-
 api/internal/handler/service/service.go        |  6 ++
 api/internal/handler/ssl/ssl.go                |  6 ++
 api/internal/handler/upstream/upstream.go      |  8 +++
 api/internal/handler/upstream/upstream_test.go |  2 +-
 api/test/e2e/id_compatible_test.go             |  2 -
 10 files changed, 222 insertions(+), 12 deletions(-)

diff --git a/api/internal/core/store/store.go b/api/internal/core/store/store.go
index bf00dcd..9a4b113 100644
--- a/api/internal/core/store/store.go
+++ b/api/internal/core/store/store.go
@@ -327,7 +327,6 @@ func (s *GenericStore) StringToObjPtr(str, key string) 
(interface{}, error) {
        objPtr := reflect.New(s.opt.ObjType)
        ret := objPtr.Interface()
        err := json.Unmarshal([]byte(str), ret)
-       fmt.Println("ret:", ret, "s.opt.ObjType", s.opt.ObjType)
        if err != nil {
                log.Errorf("json marshal failed: %s", err)
                return nil, fmt.Errorf("json unmarshal failed: %s", err)
diff --git a/api/internal/handler/handler.go b/api/internal/handler/handler.go
index 39256d9..fecd0d3 100644
--- a/api/internal/handler/handler.go
+++ b/api/internal/handler/handler.go
@@ -35,13 +35,16 @@
 package handler
 
 import (
-       "github.com/shiningrush/droplet"
-       "github.com/shiningrush/droplet/middleware"
+       "fmt"
        "net/http"
        "strings"
 
        "github.com/gin-gonic/gin"
+       "github.com/shiningrush/droplet"
        "github.com/shiningrush/droplet/data"
+       "github.com/shiningrush/droplet/middleware"
+
+       "github.com/apisix/manager-api/internal/utils"
 )
 
 type RegisterFactory func() (RouteRegister, error)
@@ -87,3 +90,17 @@ func (mw *ErrorTransformMiddleware) Handle(ctx 
droplet.Context) error {
        }
        return nil
 }
+
+func IDCompare(idOnPath string, idOnBody interface{}) error {
+       idOnBodyStr, ok := idOnBody.(string)
+       if !ok {
+               idOnBodyStr = utils.InterfaceToString(idOnBody)
+       }
+
+       // check if id on path is == to id on body ONLY if both ids are valid
+       if idOnPath != "" && idOnBodyStr != "" && idOnBodyStr != idOnPath {
+               return fmt.Errorf("ID on path (%s) doesn't match ID on body 
(%s)", idOnPath, idOnBodyStr)
+       }
+
+       return nil
+}
diff --git a/api/internal/handler/handler_test.go 
b/api/internal/handler/handler_test.go
new file mode 100644
index 0000000..7fd288c
--- /dev/null
+++ b/api/internal/handler/handler_test.go
@@ -0,0 +1,86 @@
+/*
+ * 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 handler
+
+import (
+       "errors"
+       "net/http"
+       "testing"
+
+       "github.com/go-playground/assert/v2"
+       "github.com/shiningrush/droplet/data"
+)
+
+func TestSpecCodeResponse(t *testing.T) {
+       err := errors.New("schema validate failed: remote_addr: Must validate 
at least one schema (anyOf)")
+       resp := SpecCodeResponse(err)
+       assert.Equal(t, &data.SpecCodeResponse{StatusCode: 
http.StatusBadRequest}, resp)
+
+       err = errors.New("data not found")
+       resp = SpecCodeResponse(err)
+       assert.Equal(t, &data.SpecCodeResponse{StatusCode: 
http.StatusNotFound}, resp)
+
+       err = errors.New("system error")
+       resp = SpecCodeResponse(err)
+       assert.Equal(t, &data.SpecCodeResponse{StatusCode: 
http.StatusInternalServerError}, resp)
+}
+
+func TestIDCompare(t *testing.T) {
+       // init
+       cases := []struct {
+               idOnPath, desc string
+               idOnBody       interface{}
+               wantError      error
+       }{
+               {
+                       desc:     "ID on body is int, and it could be 
considered the same as ID on path",
+                       idOnPath: "1",
+                       idOnBody: 1,
+               },
+               {
+                       desc:      "ID on body is int, and it is different from 
ID on path",
+                       idOnPath:  "1",
+                       idOnBody:  2,
+                       wantError: errors.New("ID on path (1) doesn't match ID 
on body (2)"),
+               },
+               {
+                       desc:     "ID on body is same as ID on path",
+                       idOnPath: "1",
+                       idOnBody: "1",
+               },
+               {
+                       desc:      "ID on body is different from ID on path",
+                       idOnPath:  "a",
+                       idOnBody:  "b",
+                       wantError: errors.New("ID on path (a) doesn't match ID 
on body (b)"),
+               },
+               {
+                       desc:     "No ID on body",
+                       idOnPath: "1",
+               },
+               {
+                       desc:     "No ID on path",
+                       idOnBody: 1,
+               },
+       }
+       for _, c := range cases {
+               t.Run(c.desc, func(t *testing.T) {
+                       err := IDCompare(c.idOnPath, c.idOnBody)
+                       assert.Equal(t, c.wantError, err)
+               })
+       }
+}
diff --git a/api/internal/handler/route/route.go 
b/api/internal/handler/route/route.go
index 275ab1e..59dc6ce 100644
--- a/api/internal/handler/route/route.go
+++ b/api/internal/handler/route/route.go
@@ -347,13 +347,15 @@ type UpdateInput struct {
 
 func (h *Handler) Update(c droplet.Context) (interface{}, error) {
        input := c.Input().(*UpdateInput)
-       if input.ID != "" {
-               input.Route.ID = input.ID
+
+       // check if ID in body is equal ID in path
+       if err := handler.IDCompare(input.ID, input.Route.ID); err != nil {
+               return &data.SpecCodeResponse{StatusCode: 
http.StatusBadRequest}, err
        }
 
-       if input.Route.Host != "" && len(input.Route.Hosts) > 0 {
-               return &data.SpecCodeResponse{StatusCode: 
http.StatusBadRequest},
-                       fmt.Errorf("only one of host or hosts is allowed")
+       // if has id in path, use it
+       if input.ID != "" {
+               input.Route.ID = input.ID
        }
 
        //check depend
diff --git a/api/internal/handler/route/route_test.go 
b/api/internal/handler/route/route_test.go
index 0fd67c6..f84fc90 100644
--- a/api/internal/handler/route/route_test.go
+++ b/api/internal/handler/route/route_test.go
@@ -757,6 +757,86 @@ func TestRoute(t *testing.T) {
        //sleep
        time.Sleep(time.Duration(100) * time.Millisecond)
 
+       // check ID discrepancy on Update
+
+       // Fail: test the string body id value != string route id value
+       errRoute := &UpdateInput{}
+       errRoute.ID = "2"
+       err = json.Unmarshal([]byte(reqBody), errRoute)
+       assert.Nil(t, err)
+       ctx.SetInput(errRoute)
+       ret, err = handler.Update(ctx)
+       assert.NotNil(t, err)
+       assert.EqualError(t, err, "ID on path (2) doesn't match ID on body (1)")
+       assert.Equal(t, http.StatusBadRequest, 
ret.(*data.SpecCodeResponse).StatusCode)
+
+       // Fail: tests the float body id value != string route id value
+       reqBodyErr := `{
+               "id": 1,
+               "uri": "/index.html",
+               "upstream": {
+                       "type": "roundrobin",
+                       "nodes": [{
+                               "host": "www.a.com",
+                               "port": 80,
+                               "weight": 1
+                       }]
+               }
+       }`
+       errRoute = &UpdateInput{}
+       errRoute.ID = "2"
+       err = json.Unmarshal([]byte(reqBodyErr), errRoute)
+       assert.Nil(t, err)
+       ctx.SetInput(errRoute)
+       ret, err = handler.Update(ctx)
+       assert.NotNil(t, err)
+       assert.EqualError(t, err, "ID on path (2) doesn't match ID on body (1)")
+       assert.Equal(t, http.StatusBadRequest, 
ret.(*data.SpecCodeResponse).StatusCode)
+
+       // Success: tests the float body id value is == string route id value
+       reqBodyErr = `{
+               "id": 10,
+               "uri": "/index.html",
+               "upstream": {
+                       "type": "roundrobin",
+                       "nodes": [{
+                               "host": "www.a.com",
+                               "port": 80,
+                               "weight": 1
+                       }]
+               }
+       }`
+       errRoute = &UpdateInput{}
+       errRoute.ID = "10"
+       err = json.Unmarshal([]byte(reqBodyErr), errRoute)
+       assert.Nil(t, err)
+       ctx.SetInput(errRoute)
+       ret, err = handler.Update(ctx)
+       assert.Nil(t, err)
+
+       // Success: tests the Body ID can be nil
+       reqBodyErr = `{
+               "uri": "/index.html",
+               "upstream": {
+                       "type": "roundrobin",
+                       "nodes": [{
+                               "host": "www.a.com",
+                               "port": 80,
+                               "weight": 1
+                       }]
+               }
+       }`
+       errRoute = &UpdateInput{}
+       errRoute.ID = "r1"
+       err = json.Unmarshal([]byte(reqBodyErr), errRoute)
+       assert.Nil(t, err)
+       ctx.SetInput(errRoute)
+       ret, err = handler.Update(ctx)
+       assert.Nil(t, err)
+
+       //sleep
+       time.Sleep(time.Duration(100) * time.Millisecond)
+
        //list
        listInput := &ListInput{}
        reqBody = `{"page_size": 1, "page": 1}`
@@ -1066,7 +1146,6 @@ func TestRoute(t *testing.T) {
        ctx.SetInput(inputDel)
        _, err = handler.BatchDelete(ctx)
        assert.Nil(t, err)
-
 }
 
 func Test_Route_With_Script(t *testing.T) {
@@ -1211,4 +1290,13 @@ func Test_Route_With_Script(t *testing.T) {
        assert.Nil(t, err)
        assert.Equal(t, stored.ID, route.ID)
        assert.Nil(t, stored.Script)
+
+       //delete test data
+       inputDel := &BatchDelete{}
+       reqBody = `{"ids": "1"}`
+       err = json.Unmarshal([]byte(reqBody), inputDel)
+       assert.Nil(t, err)
+       ctx.SetInput(inputDel)
+       _, err = handler.BatchDelete(ctx)
+       assert.Nil(t, err)
 }
diff --git a/api/internal/handler/service/service.go 
b/api/internal/handler/service/service.go
index 1adb0a4..799e58a 100644
--- a/api/internal/handler/service/service.go
+++ b/api/internal/handler/service/service.go
@@ -179,6 +179,12 @@ type UpdateInput struct {
 
 func (h *Handler) Update(c droplet.Context) (interface{}, error) {
        input := c.Input().(*UpdateInput)
+
+       // check if ID in body is equal ID in path
+       if err := handler.IDCompare(input.ID, input.Service.ID); err != nil {
+               return &data.SpecCodeResponse{StatusCode: 
http.StatusBadRequest}, err
+       }
+
        if input.ID != "" {
                input.Service.ID = input.ID
        }
diff --git a/api/internal/handler/ssl/ssl.go b/api/internal/handler/ssl/ssl.go
index 28408cc..0c9a3f2 100644
--- a/api/internal/handler/ssl/ssl.go
+++ b/api/internal/handler/ssl/ssl.go
@@ -203,6 +203,12 @@ type UpdateInput struct {
 
 func (h *Handler) Update(c droplet.Context) (interface{}, error) {
        input := c.Input().(*UpdateInput)
+
+       // check if ID in body is equal ID in path
+       if err := handler.IDCompare(input.ID, input.SSL.ID); err != nil {
+               return &data.SpecCodeResponse{StatusCode: 
http.StatusBadRequest}, err
+       }
+
        ssl, err := ParseCert(input.Cert, input.Key)
        if err != nil {
                return &data.SpecCodeResponse{StatusCode: 
http.StatusBadRequest}, err
diff --git a/api/internal/handler/upstream/upstream.go 
b/api/internal/handler/upstream/upstream.go
index 672906e..790fbf0 100644
--- a/api/internal/handler/upstream/upstream.go
+++ b/api/internal/handler/upstream/upstream.go
@@ -17,12 +17,14 @@
 package upstream
 
 import (
+       "net/http"
        "reflect"
        "strings"
 
        "github.com/api7/go-jsonpatch"
        "github.com/gin-gonic/gin"
        "github.com/shiningrush/droplet"
+       "github.com/shiningrush/droplet/data"
        "github.com/shiningrush/droplet/wrapper"
        wgin "github.com/shiningrush/droplet/wrapper/gin"
 
@@ -161,6 +163,12 @@ type UpdateInput struct {
 
 func (h *Handler) Update(c droplet.Context) (interface{}, error) {
        input := c.Input().(*UpdateInput)
+
+       // check if ID in body is equal ID in path
+       if err := handler.IDCompare(input.ID, input.Upstream.ID); err != nil {
+               return &data.SpecCodeResponse{StatusCode: 
http.StatusBadRequest}, err
+       }
+
        if input.ID != "" {
                input.Upstream.ID = input.ID
        }
diff --git a/api/internal/handler/upstream/upstream_test.go 
b/api/internal/handler/upstream/upstream_test.go
index e72b14f..6216633 100644
--- a/api/internal/handler/upstream/upstream_test.go
+++ b/api/internal/handler/upstream/upstream_test.go
@@ -115,7 +115,7 @@ func TestUpstream(t *testing.T) {
        upstream2 := &UpdateInput{}
        upstream2.ID = "1"
        reqBody = `{
-               "id": "aaa",
+               "id": "1",
                "name": "upstream3",
                "description": "upstream upstream",
                "type": "roundrobin",
diff --git a/api/test/e2e/id_compatible_test.go 
b/api/test/e2e/id_compatible_test.go
index 90c3322..33dc865 100644
--- a/api/test/e2e/id_compatible_test.go
+++ b/api/test/e2e/id_compatible_test.go
@@ -17,7 +17,6 @@
 package e2e
 
 import (
-       "fmt"
        "io/ioutil"
        "net/http"
        "testing"
@@ -458,7 +457,6 @@ func TestID_Not_In_Body(t *testing.T) {
        assert.Nil(t, err)
        defer resp.Body.Close()
        respBody, _ := ioutil.ReadAll(resp.Body)
-       fmt.Println("string(respBody)", string(respBody))
        list := gjson.Get(string(respBody), "data.rows").Value().([]interface{})
        for _, item := range list {
                route := item.(map[string]interface{})

Reply via email to