feat : add non-invasive user access control framework (#2589)
diff --git a/.licenserc.yaml b/.licenserc.yaml
index e6ec0ed..e3cea2b 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -45,6 +45,7 @@
- 'api/logs/.gitkeep'
- 'api/service/apisix-dashboard.service'
- 'api/VERSION'
+ - 'api/pkg/iam/demo/policy.csv'
# Other files
- 'go.mod'
diff --git a/api/config/config.yaml b/api/config/config.yaml
index 2142af9..75e990d 100644
--- a/api/config/config.yaml
+++ b/api/config/config.yaml
@@ -65,3 +65,4 @@
password: user
feature_gate:
+ demoIAMAccess: false
diff --git a/api/internal/config/config.go b/api/internal/config/config.go
index e70d7cf..70d7781 100644
--- a/api/internal/config/config.go
+++ b/api/internal/config/config.go
@@ -82,7 +82,9 @@
},
},
},
- FeatureGate: FeatureGate{},
+ FeatureGate: FeatureGate{
+ DemoIAMAccess: true,
+ },
}
}
diff --git a/api/internal/config/structs.go b/api/internal/config/structs.go
index f5412ee..fe04ee3 100644
--- a/api/internal/config/structs.go
+++ b/api/internal/config/structs.go
@@ -105,4 +105,5 @@
}
type FeatureGate struct {
+ DemoIAMAccess bool `mapstructure:"demoIAMAccess"`
}
diff --git a/api/internal/filter/authentication.go b/api/internal/filter/authentication.go
index 90323a6..f8b9d14 100644
--- a/api/internal/filter/authentication.go
+++ b/api/internal/filter/authentication.go
@@ -84,6 +84,7 @@
return
}
+ c.Set("identity", claims.Subject)
c.Next()
}
}
diff --git a/api/internal/filter/iam/iam.go b/api/internal/filter/iam/iam.go
new file mode 100644
index 0000000..296d691
--- /dev/null
+++ b/api/internal/filter/iam/iam.go
@@ -0,0 +1,67 @@
+/*
+ * 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 iam
+
+import (
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+
+ "github.com/apache/apisix-dashboard/api/internal/config"
+ iamDef "github.com/apache/apisix-dashboard/api/pkg/iam"
+ "github.com/apache/apisix-dashboard/api/pkg/iam/demo"
+)
+
+var (
+ access iamDef.Access
+ accessLock bool
+)
+
+func Filter(cfg config.Config) gin.HandlerFunc {
+ // When feature gate demoIAMAccess is configured to be on,
+ // set the access implementation to Demo
+ if cfg.FeatureGate.DemoIAMAccess {
+ access = demo.Access{}
+ accessLock = true
+ }
+
+ return func(c *gin.Context) {
+ if access != nil && c.Request.URL.Path != "/apisix/admin/user/login" {
+ identity := c.MustGet("identity").(string)
+ err := access.Check(identity, c.Request.URL.Path, c.Request.Method)
+ if err != nil {
+ c.AbortWithStatus(http.StatusForbidden)
+ return
+ }
+ }
+
+ c.Next()
+ }
+}
+
+// SetAccessImplementation provides a function that allows developers to replace the built-in access control implementation
+// This function is allowed to be called only once and returns true on success.
+// After setting, the access implementation will be locked and another attempt to set it will return false.
+func SetAccessImplementation(impl iamDef.Access) bool {
+ if accessLock {
+ return false
+ }
+ access = impl
+ accessLock = true
+ return true
+}
diff --git a/api/internal/filter/iam/iam_test.go b/api/internal/filter/iam/iam_test.go
new file mode 100644
index 0000000..1447f56
--- /dev/null
+++ b/api/internal/filter/iam/iam_test.go
@@ -0,0 +1,99 @@
+/*
+ * 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 iam
+
+import (
+ "errors"
+ "github.com/apache/apisix-dashboard/api/internal/config"
+ "github.com/apache/apisix-dashboard/api/pkg/iam"
+ "github.com/gin-gonic/gin"
+ "github.com/stretchr/testify/assert"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+)
+
+var cfg = config.NewDefaultConfig()
+
+func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
+ req := httptest.NewRequest(method, path, nil)
+ w := httptest.NewRecorder()
+ r.ServeHTTP(w, req)
+ return w
+}
+
+func TestFilter(t *testing.T) {
+ r := gin.Default()
+ r.Use(func(c *gin.Context) {
+ c.Set("identity", "user")
+ })
+ r.Use(Filter(cfg))
+ r.POST("/apisix/admin/user/login", func(ctx *gin.Context) {})
+ r.PUT("/apisix/admin/global_rules/:id", func(ctx *gin.Context) {})
+ r.DELETE("/apisix/admin/stream_routes/:ids", func(ctx *gin.Context) {})
+ r.GET("/*path", func(ctx *gin.Context) {})
+ r.POST("/success", func(ctx *gin.Context) {})
+
+ w := performRequest(r, http.MethodPost, "/apisix/admin/user/login")
+ assert.Equal(t, http.StatusOK, w.Code)
+
+ w = performRequest(r, http.MethodDelete, "/apisix/admin/global_rules/12")
+ assert.Equal(t, http.StatusForbidden, w.Code)
+
+ w = performRequest(r, http.MethodDelete, "/apisix/admin/stream_routes/67")
+ assert.Equal(t, http.StatusForbidden, w.Code)
+
+ w = performRequest(r, http.MethodGet, "/apisix/admin/ssl/98")
+ assert.Equal(t, http.StatusOK, w.Code)
+
+ w = performRequest(r, http.MethodPost, "/success")
+ assert.Equal(t, http.StatusOK, w.Code)
+}
+
+type test struct{}
+
+var _ iam.Access = test{}
+
+func (test) Check(identity, resource, action string) error {
+ return errors.New("no permission")
+}
+
+func TestSetAccessImplementation(t *testing.T) {
+ // close the default gate to use the customized one
+ cfg.FeatureGate.DemoIAMAccess = false
+ // because the last test. we should reset the value
+ accessLock = false
+ SetAccessImplementation(test{})
+ r := gin.Default()
+ r.Use(func(c *gin.Context) {
+ c.Set("identity", "user")
+ })
+ r.Use(Filter(cfg))
+ r.POST("/apisix/admin/user/login", func(ctx *gin.Context) {})
+ r.PUT("/apisix/admin/route/:id", func(ctx *gin.Context) {})
+ r.DELETE("/apisix/admin/upstream", func(ctx *gin.Context) {})
+
+ w := performRequest(r, http.MethodPost, "/apisix/admin/user/login")
+ assert.Equal(t, http.StatusOK, w.Code)
+
+ w = performRequest(r, http.MethodPut, "/apisix/admin/route/2")
+ assert.Equal(t, http.StatusForbidden, w.Code)
+
+ w = performRequest(r, http.MethodDelete, "/apisix/admin/upstream")
+ assert.Equal(t, http.StatusForbidden, w.Code)
+}
diff --git a/api/internal/route.go b/api/internal/route.go
index 486004d..b20524c 100644
--- a/api/internal/route.go
+++ b/api/internal/route.go
@@ -31,6 +31,7 @@
"github.com/apache/apisix-dashboard/api/internal/config"
"github.com/apache/apisix-dashboard/api/internal/filter"
+ "github.com/apache/apisix-dashboard/api/internal/filter/iam"
"github.com/apache/apisix-dashboard/api/internal/handler"
"github.com/apache/apisix-dashboard/api/internal/log"
)
@@ -43,8 +44,15 @@
}
r := gin.New()
logger := log.GetLogger(log.AccessLog)
+
// security
- r.Use(filter.RequestLogHandler(logger), filter.IPFilter(cfg.Security), filter.InvalidRequest(), filter.Authentication(cfg.Authentication))
+ r.Use(
+ filter.RequestLogHandler(logger),
+ filter.IPFilter(cfg.Security),
+ filter.InvalidRequest(),
+ filter.Authentication(cfg.Authentication),
+ iam.Filter(cfg),
+ )
// misc
staticPath := "./html/"
diff --git a/api/pkg/iam/demo/access.go b/api/pkg/iam/demo/access.go
new file mode 100644
index 0000000..dc939cc
--- /dev/null
+++ b/api/pkg/iam/demo/access.go
@@ -0,0 +1,101 @@
+/*
+ * 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 demo
+
+import (
+ "embed"
+ "errors"
+ "strings"
+
+ "github.com/casbin/casbin/v2"
+ casbinFSAdapter "github.com/naucon/casbin-fs-adapter"
+
+ "github.com/apache/apisix-dashboard/api/pkg/iam"
+)
+
+var (
+ //go:embed model.conf policy.csv
+ fs embed.FS
+
+ // Ensure that demo Access conforms to the iam.Access interface definition
+ _ iam.Access = Access{}
+)
+
+type Access struct{}
+
+func (Access) Check(identity, resource, action string) error {
+ // Load casbin model and adapter from Go embed FS
+ model, _ := casbinFSAdapter.NewModel(fs, "model.conf")
+ policies := casbinFSAdapter.NewAdapter(fs, "policy.csv")
+ policies.LoadPolicy(model)
+ // Create enforcer
+ enforce, err := casbin.NewEnforcer(model, policies)
+ if err != nil {
+ return err
+ }
+ enforce.AddFunction("identify", KeyMatchFunc)
+ // get all the permission the user has
+ pers, _ := enforce.GetImplicitPermissionsForUser("role_admin")
+ admin, _ := enforce.HasRoleForUser(identity, "role_admin")
+ if !admin {
+ for _, v := range pers {
+ if KeyMatch(resource, v[1]) && action == v[2] {
+ return errors.New("no permission")
+ }
+ }
+ }
+
+ // the normal url can be requested
+ return nil
+}
+
+// KeyMatchFunc wrap KeyMatch to meet with casbin's need of custom functions
+func KeyMatchFunc(args ...interface{}) (interface{}, error) {
+ key1, key2 := args[0].(string), args[1].(string)
+ return (bool)(KeyMatch(key1, key2)), nil
+}
+
+// KeyMatch can match three patterns of route /* && /:id && /:id/*
+func KeyMatch(key1 string, key2 string) bool {
+ i, j := strings.Index(key2, ":"), strings.Index(key2, "*")
+ if len(key1) < i+1 {
+ return false
+ }
+ if i != -1 {
+ ok := key1[:i-1] == key2[:i-1]
+ if j != -1 && ok {
+ k, p := strings.Index(key2[i:], "/"), strings.Index(key1[i:], "/")
+ if key2[i+k+1] == '*' {
+ return true
+ }
+ return key2[i+k:j] == key1[i+p:i+p+k+1]
+ }
+ return ok
+ } else if j != -1 {
+ ok := key1[:j-1] == key2[:j-1]
+ if i != -1 && ok {
+ k, p := strings.Index(key2[i:], "/"), strings.Index(key1[i:], "/")
+ if key2[i+k+1] == '*' {
+ return true
+ }
+ return key2[i+k:j] == key1[i+p:i+p+k+1]
+ }
+ return ok
+ }
+ return key1 == key2
+}
diff --git a/api/pkg/iam/demo/model.conf b/api/pkg/iam/demo/model.conf
new file mode 100644
index 0000000..875617a
--- /dev/null
+++ b/api/pkg/iam/demo/model.conf
@@ -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.
+#
+
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub, obj, act
+
+[role_definition]
+g = _, _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub) && (r.obj == p.obj || identify(r.obj,p.obj)) && r.act == p.act
diff --git a/api/pkg/iam/demo/policy.csv b/api/pkg/iam/demo/policy.csv
new file mode 100644
index 0000000..3a8c406
--- /dev/null
+++ b/api/pkg/iam/demo/policy.csv
@@ -0,0 +1,53 @@
+p,role_admin,/apisix/admin/ssl,POST
+p,role_admin,/apisix/admin/services,POST
+p,role_admin,/apisix/admin/stream_routes,POST
+p,role_admin,/apisix/admin/system_config,POST
+p,role_admin,/apisix/admin/upstreams,POST
+p,role_admin,/apisix/admin/plugin_configs,POST
+p,role_admin,/apisix/admin/proto,POST
+p,role_admin,/apisix/admin/routes,POST
+p,role_admin,/apisix/admin/import/routes,POST
+p,role_admin,/apisix/admin/ssl,PUT
+p,role_admin,/apisix/admin/ssl/:id,PUT
+p,role_admin,/apisix/admin/services,PUT
+p,role_admin,/apisix/admin/services/:id,PUT
+p,role_admin,/apisix/admin/stream_routes,PUT
+p,role_admin,/apisix/admin/stream_routes/:id,PUT
+p,role_admin,/apisix/admin/system_config,PUT
+p,role_admin,/apisix/admin/plugin_configs,PUT
+p,role_admin,/apisix/admin/plugin_configs/:id,PUT
+p,role_admin,/apisix/admin/proto,PUT
+p,role_admin,/apisix/admin/proto/:id,PUT
+p,role_admin,/apisix/admin/routes,PUT
+p,role_admin,/apisix/admin/routes/:id,PUT
+p,role_admin,/apisix/admin/consumers,PUT
+p,role_admin,/apisix/admin/consumers/:username,PUT
+p,role_admin,/apisix/admin/upstreams,PUT
+p,role_admin,/apisix/admin/upstreams/:id,PUT
+p,role_admin,/apisix/admin/global_rules,PUT
+p,role_admin,/apisix/admin/global_rules/:id,PUT
+p,role_admin,/apisix/admin/ssl/:ids,DELETE
+p,role_admin,/apisix/admin/services/:ids,DELETE
+p,role_admin,/apisix/admin/stream_routes/:ids,DELETE
+p,role_admin,/apisix/admin/system_config/:config_name,DELETE
+p,role_admin,/apisix/admin/plugin_configs/:ids,DELETE
+p,role_admin,/apisix/admin/proto/:ids,DELETE
+p,role_admin,/apisix/admin/routes/:ids,DELETE
+p,role_admin,/apisix/admin/consumers/:usernames,DELETE
+p,role_admin,/apisix/admin/upstreams/:ids,DELETE
+p,role_admin,/apisix/admin/global_rules/:id,DELETE
+p,role_admin,/apisix/admin/ssl/:id,PATCH
+p,role_admin,/apisix/admin/ssl/:id/*path,PATCH
+p,role_admin,/apisix/admin/services/:id,PATCH
+p,role_admin,/apisix/admin/services/:id/*path,PATCH
+p,role_admin,/apisix/admin/plugin_configs/:id,PATCH
+p,role_admin,/apisix/admin/plugin_configs/:id/*path,PATCH
+p,role_admin,/apisix/admin/proto/:id,PATCH
+p,role_admin,/apisix/admin/proto/:id/*path,PATCH
+p,role_admin,/apisix/admin/routes/:id,PATCH
+p,role_admin,/apisix/admin/routes/:id/*path,PATCH
+p,role_admin,/apisix/admin/upstreams/:id,PATCH
+p,role_admin,/apisix/admin/upstreams/:id/*path,PATCH
+p,role_admin,/apisix/admin/global_rules/:id,PATCH
+p,role_admin,/apisix/admin/global_rules/:id/*path,PATCH
+g,user_admin,role_admin
diff --git a/api/pkg/iam/interface.go b/api/pkg/iam/interface.go
new file mode 100644
index 0000000..ab5deca
--- /dev/null
+++ b/api/pkg/iam/interface.go
@@ -0,0 +1,23 @@
+/*
+ * 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 iam
+
+// Access interface defines the pattern of functions required for access control in the IAM
+type Access interface {
+ Check(identity, resource, action string) error
+}
diff --git a/api/test/shell/cli_test.sh b/api/test/shell/cli_test.sh
index 2c10463..ebbdba1 100755
--- a/api/test/shell/cli_test.sh
+++ b/api/test/shell/cli_test.sh
@@ -431,6 +431,45 @@
stop_dashboard 6
}
+#15
+@test "Check FeatureGate effected" {
+ recover_conf
+ start_dashboard 3
+
+ # validate process is right by requesting login api
+ run curl http://127.0.0.1:9000/apisix/admin/user/login -H "Content-Type: application/json" -d '{"username":"user", "password": "user"}'
+ token=$(echo "$output" | sed 's/{/\n/g' | sed 's/,/\n/g' | grep "token" | sed 's/:/\n/g' | sed '1d' | sed 's/}//g' | sed 's/"//g')
+
+ [ -n "${token}" ]
+
+ # more validation to make sure it's ok to access etcd
+ run curl -ig -XPUT http://127.0.0.1:9000/apisix/admin/consumers -i -H "Content-Type: application/json" -H "Authorization: $token" -d '{"username":"etcd_basic_auth_test"}'
+ respCode=$(echo "$output" | sed 's/{/\n/g'| sed 's/,/\n/g' | grep "code" | sed 's/:/\n/g' | sed '1d')
+
+ [ "$respCode" = "0" ]
+
+ stop_dashboard 6
+
+ recover_conf
+ yq -y '.feature_gate.demoIAMAccess=true' config/config.yaml > config/config.yaml.tmp && mv config/config.yaml.tmp ${CONF_FILE}
+
+ start_dashboard 3
+
+ # validate process is right by requesting login api
+ run curl http://127.0.0.1:9000/apisix/admin/user/login -H "Content-Type: application/json" -d '{"username":"user", "password": "user"}'
+ token=$(echo "$output" | sed 's/{/\n/g' | sed 's/,/\n/g' | grep "token" | sed 's/:/\n/g' | sed '1d' | sed 's/}//g' | sed 's/"//g')
+
+ [ -n "${token}" ]
+
+ # more validation to make sure it's ok to access etcd
+ run curl -ig -XPUT http://127.0.0.1:9000/apisix/admin/consumers -i -H "Content-Type: application/json" -H "Authorization: $token" -d '{"username":"etcd_basic_auth_test"}'
+ respCode=$(echo "$output" | sed 's/{/\n/g'| sed 's/,/\n/g' | grep "code" | sed 's/:/\n/g' | sed '1d')
+
+ [ "$respCode" != "0" ]
+
+ stop_dashboard 6
+}
+
#post
@test "Clean test environment" {
# kill etcd
diff --git a/docs/en/latest/FAQ.md b/docs/en/latest/FAQ.md
index 52133c0..4ade2eb 100644
--- a/docs/en/latest/FAQ.md
+++ b/docs/en/latest/FAQ.md
@@ -79,8 +79,8 @@
```yaml
conf:
- allow_list:
- - 0.0.0.0/0
+ allow_list:
+ - 0.0.0.0/0
```
2. Allow all IPv6 access
@@ -99,7 +99,7 @@
```yaml
conf:
- allow_list:
+ allow_list:
```
Restart `manager-api`, all IPs can access `APISIX Dashboard`.
@@ -135,4 +135,4 @@
If the domain name of the address is configured as HTTPS, the embedded grafana will jump to the login page after logging in. You can refer to this solution:
-It's best for Grafana to configure the domain name in the same way. Otherwise there will be problems with address resolution.
+It's best for Grafana to configure the domain name in the same way. Otherwise there will be problems with address resolution.
\ No newline at end of file
diff --git a/docs/en/latest/backend-authentication.md b/docs/en/latest/backend-authentication.md
new file mode 100644
index 0000000..8605aaa
--- /dev/null
+++ b/docs/en/latest/backend-authentication.md
@@ -0,0 +1,45 @@
+### How to use this Non-intrusive framework
+
+- **target**: we can use this framework to achieve adding customized authentication method to dashboard
+- **implementation**: we use middleware to check user permissions. The way to check can be decided by developer, if not, we provide a default way for developers
+- **usage**:
+
+1.we open the switch to adapt the **default authentication method**:
+
+```shell
+feature_gate:
+ demoIAMAccess:true
+```
+
+ the default strategy where we use the `casbin` framework to achieve. Also, we can add and delete this route in **`internal/pkg/iam/demo/policy.csv`** that only can be accessed by **admin**
+
+2.Adopt a **customized authentication method**
+
+```shell
+# at first, we should close this switch to support customized authentication method
+feature_gate:
+ demoIAMAcess:false
+```
+
+```go
+// then, we should create struct to implement this interface
+// parameters explanation. identity -> username(user or admin) resource -> url action -> method
+// in the method Check, you can customize some way to authenticate these interviewers
+// if interviewers aren't permitted to request this resource. you can throw an error
+type Access interface {
+ Check(identity, resource, action string) error
+}
+
+type MyAccess struct{}
+func (m MyAccess)Check(identity, resource, action string) error {
+ // customized way
+}
+func main(){
+ // add your customized method into APISIX-DashBoard
+ ok := SetAccessImplementation(MyAccess{})
+ if ok {
+ // add successfully
+ } else {
+ // there is an existing method in dashboard
+}
+```
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 4dba40c..c6edf75 100644
--- a/go.mod
+++ b/go.mod
@@ -2,10 +2,18 @@
go 1.18
+replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
+
+replace github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.5
+
require (
+ github.com/casbin/casbin/v2 v2.36.1
github.com/evanphx/json-patch/v5 v5.1.0
github.com/gin-contrib/gzip v0.0.3
github.com/gin-contrib/static v0.0.0-20200916080430-d45d9a37d28e
+ github.com/juliangruber/go-intersect v1.1.0
+ github.com/naucon/casbin-fs-adapter v0.1.0
+ github.com/shiningrush/droplet v0.2.6-0.20210127040147-53817015cd1b
github.com/gin-gonic/gin v1.8.1
github.com/go-playground/validator/v10 v10.10.0
github.com/go-resty/resty/v2 v2.7.0
@@ -13,16 +21,55 @@
github.com/pkg/errors v0.9.1
github.com/satori/go.uuid v1.2.0
github.com/shiningrush/droplet v0.2.6-0.20210127040147-53817015cd1b
+ github.com/shiningrush/droplet/wrapper/gin v0.2.1
github.com/sony/sonyflake v1.0.0
github.com/spf13/cobra v0.0.3
github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.7.1
github.com/tidwall/gjson v1.6.7
+ github.com/xeipuuv/gojsonschema v1.2.0
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da
go.uber.org/zap v1.22.0
)
require (
+ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
+ github.com/coreos/bbolt v1.3.2 // indirect
+ github.com/coreos/etcd v3.3.25+incompatible // indirect
+ github.com/coreos/go-semver v0.3.0 // indirect
+ github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
+ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/dustin/go-humanize v1.0.0 // indirect
+ github.com/fsnotify/fsnotify v1.4.9 // indirect
+ github.com/ghodss/yaml v1.0.0 // indirect
+ github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/go-openapi/jsonpointer v0.19.5 // indirect
+ github.com/go-openapi/swag v0.19.5 // indirect
+ github.com/go-playground/locales v0.13.0 // indirect
+ github.com/go-playground/universal-translator v0.17.0 // indirect
+ github.com/go-playground/validator/v10 v10.3.0 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/golang/protobuf v1.5.2 // indirect
+ github.com/google/uuid v1.2.0 // indirect
+ github.com/gorilla/websocket v1.4.2 // indirect
+ github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 // indirect
+ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
+ github.com/hashicorp/hcl v1.0.0 // indirect
+ github.com/inconshreveable/mousetrap v1.0.0 // indirect
+ github.com/jonboulle/clockwork v0.2.2 // indirect
+ github.com/json-iterator/go v1.1.11 // indirect
+ github.com/leodido/go-urn v1.2.0 // indirect
+ github.com/magiconair/properties v1.8.5 // indirect
+ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e // indirect
+ github.com/mattn/go-isatty v0.0.12 // indirect
+ github.com/mitchellh/mapstructure v1.4.1 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.1 // indirect
+ github.com/pelletier/go-toml v1.9.3 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/prometheus/client_golang v1.8.0 // indirect
+ github.com/sirupsen/logrus v1.7.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
@@ -49,6 +96,25 @@
github.com/subosito/gotenv v1.2.0 // indirect
github.com/tidwall/match v1.0.3 // indirect
github.com/tidwall/pretty v1.0.2 // indirect
+ github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966 // indirect
+ github.com/ugorji/go/codec v1.1.7 // indirect
+ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+ go.uber.org/atomic v1.7.0 // indirect
+ go.uber.org/multierr v1.6.0 // indirect
+ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
+ golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect
+ golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 // indirect
+ golang.org/x/text v0.3.6 // indirect
+ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
+ google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
+ google.golang.org/grpc v1.38.0 // indirect
+ google.golang.org/protobuf v1.26.0 // indirect
+ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
+ gopkg.in/ini.v1 v1.62.0 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ sigs.k8s.io/yaml v1.2.0 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
diff --git a/go.sum b/go.sum
index 9cad34a..99a5f13 100644
--- a/go.sum
+++ b/go.sum
@@ -39,11 +39,34 @@
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
+github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
+github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
+github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
+github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
+github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
+github.com/casbin/casbin/v2 v2.36.1 h1:6b7PQuOEcNR4ZGvQcN82+E1o/n2KMNSUk+np9iryU8A=
+github.com/casbin/casbin/v2 v2.36.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -89,6 +112,16 @@
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
@@ -122,6 +155,7 @@
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -249,6 +283,33 @@
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
+github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
+github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/naucon/casbin-fs-adapter v0.1.0 h1:eCXphqrNmk9o/DhcT8s+840goaqarhbfol/7/nRvxXw=
+github.com/naucon/casbin-fs-adapter v0.1.0/go.mod h1:PB2sx43snq6cf9VHjcCdg2ZEL6bEQKv+pPUu0GK6OyE=
+github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
+github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
+github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
+github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -313,6 +374,9 @@
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966 h1:j6JEOq5QWFker+d7mFQYOhjTZonQ7YkLTHm56dbn+yM=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
@@ -325,6 +389,12 @@
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da h1:NimzV1aGyq29m5ukMK0AMWEhFaL/lrEOaephfuoiARg=
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
+go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.etcd.io/etcd v3.3.25+incompatible h1:V1RzkZJj9LqsJRy+TUBgpWSbZXITLB819lstuTFoZOY=
+go.etcd.io/etcd v3.3.25+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
@@ -338,6 +408,8 @@
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
@@ -526,6 +598,8 @@
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=