You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2021/05/29 11:08:19 UTC

[servicecomb-service-center] branch master updated: SCB-2176 Fix: targetResource label key not exist should be pass (#1019)

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

littlecui pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-service-center.git


The following commit(s) were added to refs/heads/master by this push:
     new d9b8ba8  SCB-2176 Fix: targetResource label key not exist should be pass (#1019)
d9b8ba8 is described below

commit d9b8ba8fea32d852bdb5a79b04cb82e654169660
Author: little-cui <su...@qq.com>
AuthorDate: Sat May 29 19:08:12 2021 +0800

    SCB-2176 Fix: targetResource label key not exist should be pass (#1019)
    
    * SCB-2176 Fix: targetResource label key not exist should be pass
    
    * SCB-2176 Refactor parse UTs
    
    * SCB-2176 Resolve comments
---
 pkg/rest/util.go                             |  19 ++
 server/plugin/auth/buildin/parser.go         |  34 ++-
 server/plugin/auth/buildin/parser_test.go    | 318 ++++++++++++++++++++-------
 server/plugin/auth/buildin/service_parser.go | 231 +++++++++++++------
 server/plugin/auth/types.go                  |   4 +-
 server/response/response.go                  |   1 -
 server/service/rbac/rbac.go                  |   2 +-
 server/service/rbac/resource.go              |   2 +-
 8 files changed, 446 insertions(+), 165 deletions(-)

diff --git a/pkg/rest/util.go b/pkg/rest/util.go
index b5b108d..d707b55 100644
--- a/pkg/rest/util.go
+++ b/pkg/rest/util.go
@@ -18,7 +18,10 @@
 package rest
 
 import (
+	"bytes"
 	"encoding/json"
+	"errors"
+	"io/ioutil"
 	"net/http"
 
 	"github.com/apache/servicecomb-service-center/pkg/log"
@@ -26,6 +29,8 @@ import (
 	"github.com/go-chassis/cari/discovery"
 )
 
+var errNilRequestBody = errors.New("request body is nil")
+
 func WriteError(w http.ResponseWriter, code int32, detail string) {
 	err := discovery.NewError(code, detail)
 	w.Header().Set(HeaderContentType, ContentTypeJSON)
@@ -78,3 +83,17 @@ func WriteResponse(w http.ResponseWriter, r *http.Request, resp *discovery.Respo
 func WriteSuccess(w http.ResponseWriter, r *http.Request) {
 	WriteResponse(w, r, nil, nil)
 }
+
+// ReadBody can re-read the request body
+func ReadBody(r *http.Request) ([]byte, error) {
+	if r.Body == nil {
+		return nil, errNilRequestBody
+	}
+
+	data, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return nil, err
+	}
+	r.Body = ioutil.NopCloser(bytes.NewReader(data))
+	return data, nil
+}
diff --git a/server/plugin/auth/buildin/parser.go b/server/plugin/auth/buildin/parser.go
index 821f251..1cd3e71 100644
--- a/server/plugin/auth/buildin/parser.go
+++ b/server/plugin/auth/buildin/parser.go
@@ -18,6 +18,7 @@
 package buildin
 
 import (
+	"errors"
 	"fmt"
 	"github.com/apache/servicecomb-service-center/pkg/log"
 	"github.com/apache/servicecomb-service-center/pkg/rest"
@@ -28,20 +29,23 @@ import (
 	"strings"
 )
 
-var (
-	//TODO ...
-	APIAccountList = "/v4/accounts"
-	APIRoleList    = "/v4/roles"
-	APIOps         = "/v4/:project/admin"
-	APIGov         = "/v1/:project/gov"
-)
+var ErrCtxMatchPatternNotFound = errors.New("CtxMatchPattern not found")
 
 var APIMapping = map[string]ParseFunc{}
 
-type ParseFunc func(r *http.Request) ([]map[string]string, error)
+type ParseFunc func(r *http.Request) (*auth.ResourceScope, error)
 
-func ApplyAll(_ *http.Request) ([]map[string]string, error) {
-	return nil, nil
+// ApplyAll work when no api registered by RegisterParseFunc matched
+func ApplyAll(r *http.Request) (*auth.ResourceScope, error) {
+	apiPath, ok := r.Context().Value(rest.CtxMatchPattern).(string)
+	if !ok {
+		log.Error("CtxMatchPattern not found", nil)
+		return nil, ErrCtxMatchPatternNotFound
+	}
+	return &auth.ResourceScope{
+		Type: rbacmodel.GetResource(apiPath),
+		Verb: rbac.MethodToVerbs[r.Method],
+	}, nil
 }
 
 func FromRequest(r *http.Request) *auth.ResourceScope {
@@ -51,17 +55,11 @@ func FromRequest(r *http.Request) *auth.ResourceScope {
 		return nil
 	}
 
-	resource := rbacmodel.GetResource(apiPath)
-	labels, err := GetAPIParseFunc(apiPath)(r)
+	resource, err := GetAPIParseFunc(apiPath)(r)
 	if err != nil {
 		log.Error(fmt.Sprintf("parse from request failed"), err)
-		return nil
-	}
-	return &auth.ResourceScope{
-		Type:   resource,
-		Labels: labels,
-		Verb:   rbac.MethodToVerbs[r.Method],
 	}
+	return resource
 }
 
 func GetAPIParseFunc(apiPattern string) ParseFunc {
diff --git a/server/plugin/auth/buildin/parser_test.go b/server/plugin/auth/buildin/parser_test.go
index 418747e..fa7eeef 100644
--- a/server/plugin/auth/buildin/parser_test.go
+++ b/server/plugin/auth/buildin/parser_test.go
@@ -18,20 +18,26 @@
 package buildin_test
 
 import (
+	_ "github.com/apache/servicecomb-service-center/test"
+
 	"context"
 	"net/http"
 	"strings"
 	"testing"
 
-	_ "github.com/apache/servicecomb-service-center/test"
-
 	"github.com/apache/servicecomb-service-center/datasource"
+	"github.com/apache/servicecomb-service-center/pkg/rest"
+	"github.com/apache/servicecomb-service-center/pkg/util"
+	"github.com/apache/servicecomb-service-center/server/plugin/auth"
 	"github.com/apache/servicecomb-service-center/server/plugin/auth/buildin"
+	"github.com/apache/servicecomb-service-center/server/service/rbac"
 	"github.com/go-chassis/cari/discovery"
 	"github.com/stretchr/testify/assert"
 )
 
 func TestGetAPIParseFunc(t *testing.T) {
+	rbac.InitResourceMap()
+
 	var serviceIDA, serviceIDB string
 
 	response, _ := datasource.Instance().RegisterService(context.Background(), &discovery.CreateServiceRequest{
@@ -49,85 +55,235 @@ func TestGetAPIParseFunc(t *testing.T) {
 	})
 	serviceIDB = response.ServiceId
 
-	t.Run("get all services api should return no labels", func(t *testing.T) {
-		request, err := http.NewRequest(http.MethodGet, "/v4/default/registry/microservices", nil)
-		assert.NoError(t, err)
-
-		labels, err := buildin.GetAPIParseFunc("/v4/:project/registry/microservices")(request)
-		assert.NoError(t, err)
-		assert.Nil(t, labels)
-	})
-
-	t.Run("create services api without body should return err", func(t *testing.T) {
-		reader := strings.NewReader("{}")
-
-		request, err := http.NewRequest(http.MethodPost, "/v4/default/registry/microservices", reader)
-		assert.NoError(t, err)
-
-		_, err = buildin.GetAPIParseFunc("/v4/:project/registry/microservices")(request)
-		assert.Error(t, err)
-	})
-
-	t.Run("create services api with serviceName A should return A", func(t *testing.T) {
-		reader := strings.NewReader("{\n  \"service\": {\n    \"serviceName\": \"A\"\n  }\n}")
-
-		request, err := http.NewRequest(http.MethodPost, "/v4/default/registry/microservices", reader)
-		assert.NoError(t, err)
-
-		labels, err := buildin.GetAPIParseFunc("/v4/:project/registry/microservices")(request)
-		assert.NoError(t, err)
-		assert.Equal(t, 1, len(labels))
-		assert.Equal(t, "A", labels[0]["serviceName"])
-	})
-
-	t.Run("delete 2 services api should return 2 labels", func(t *testing.T) {
-		reader := strings.NewReader("{\n  \"serviceIds\": [\"" + serviceIDA + "\", \"" + serviceIDB + "\"]\n}")
-
-		request, err := http.NewRequest(http.MethodDelete, "/v4/default/registry/microservices", reader)
-		assert.NoError(t, err)
-
-		labels, err := buildin.GetAPIParseFunc("/v4/:project/registry/microservices")(request)
-		assert.NoError(t, err)
-		assert.Equal(t, 2, len(labels))
-		assert.Equal(t, "A", labels[0]["serviceName"])
-	})
-
-	t.Run("get service sub resource api should return service labels", func(t *testing.T) {
-		request, err := http.NewRequest(http.MethodGet, "/v4/default/registry/microservices/"+serviceIDA+"/instances?:serviceId="+serviceIDA, nil)
-		assert.NoError(t, err)
-
-		labels, err := buildin.GetAPIParseFunc("/v4/:project/registry/microservices/:serviceId/instances")(request)
-		assert.NoError(t, err)
-		assert.Equal(t, 1, len(labels))
-		assert.Equal(t, "A", labels[0]["serviceName"])
-	})
-
-	t.Run("discovery A instances api should return service A labels", func(t *testing.T) {
-		request, err := http.NewRequest(http.MethodGet, "/v4/default/registry/instances?serviceName=A", nil)
-		assert.NoError(t, err)
-
-		labels, err := buildin.GetAPIParseFunc("/v4/:project/registry/instances")(request)
-		assert.NoError(t, err)
-		assert.Equal(t, 1, len(labels))
-		assert.Equal(t, "A", labels[0]["serviceName"])
-	})
-
-	t.Run("govern query A api should return service A labels", func(t *testing.T) {
-		request, err := http.NewRequest(http.MethodGet, "/v4/default/govern/microservices?serviceName=A", nil)
-		assert.NoError(t, err)
-
-		labels, err := buildin.GetAPIParseFunc("/v4/:project/govern/microservices")(request)
-		assert.NoError(t, err)
-		assert.Equal(t, 1, len(labels))
-		assert.Equal(t, "A", labels[0]["serviceName"])
-	})
+	newRequest := func(method, url string, body string) *http.Request {
+		request, _ := http.NewRequest(method, url, strings.NewReader(body))
+		util.SetRequestContext(request, rest.CtxMatchPattern, url)
+		return request
+	}
+	tests := []struct {
+		name         string
+		request      *http.Request
+		wantResource *auth.ResourceScope
+		wantErr      bool
+	}{
+		{
+			"get all services api should return no labels",
+			newRequest(http.MethodGet, "/v4/:project/registry/microservices", ""),
+			&auth.ResourceScope{
+				Type: "service",
+				Verb: "get",
+			},
+			false,
+		},
+		{
+			"create services api without body should return err",
+			newRequest(http.MethodPost, "/v4/:project/registry/microservices", "{}"),
+			nil,
+			true,
+		},
+		{
+			"create services api with serviceName A should return A",
+			newRequest(http.MethodPost, "/v4/:project/registry/microservices",
+				"{\n  \"service\": {\n    \"serviceName\": \"A\"\n  }\n}"),
+			&auth.ResourceScope{
+				Type: "service",
+				Labels: []map[string]string{
+					{
+						"environment": "",
+						"appId":       "",
+						"serviceName": "A",
+					},
+				},
+				Verb: "create",
+			},
+			false,
+		},
+		{
+			"delete 2 services api should return 2 labels",
+			newRequest(http.MethodDelete, "/v4/:project/registry/microservices",
+				"{\n  \"serviceIds\": [\""+serviceIDA+"\", \""+serviceIDB+"\"]\n}"),
+			&auth.ResourceScope{
+				Type: "service",
+				Labels: []map[string]string{
+					{
+						"environment": "",
+						"appId":       "TestGetAPIParseFunc",
+						"serviceName": "A",
+					},
+					{
+						"environment": "",
+						"appId":       "TestGetAPIParseFunc",
+						"serviceName": "B",
+					},
+				},
+				Verb: "delete",
+			},
+			false,
+		},
+		{
+			"get service sub resource api should return service labels",
+			newRequest(http.MethodGet, "/v4/:project/registry/microservices/:serviceId/instances?:serviceId="+serviceIDA, ""),
+			&auth.ResourceScope{
+				Type: "service",
+				Labels: []map[string]string{
+					{
+						"environment": "",
+						"appId":       "TestGetAPIParseFunc",
+						"serviceName": "A",
+					},
+				},
+				Verb: "get",
+			},
+			false,
+		},
+		{
+			"discovery A instances api should return service A labels",
+			newRequest(http.MethodGet, "/v4/:project/registry/instances?serviceName=A", ""),
+			&auth.ResourceScope{
+				Type: "service",
+				Labels: []map[string]string{
+					{
+						"environment": "",
+						"appId":       "",
+						"serviceName": "A",
+					},
+				},
+				Verb: "get",
+			},
+			false,
+		},
+		{
+			"govern query A api should return service A labels",
+			newRequest(http.MethodGet, "/v4/:project/govern/microservices?serviceName=A", ""),
+			&auth.ResourceScope{
+				Type: "service",
+				Verb: "get",
+			},
+			false,
+		},
+		{
+			"govern query all api should return nil labels",
+			newRequest(http.MethodGet, "/v4/:project/govern/microservices", ""),
+			&auth.ResourceScope{
+				Type: "service",
+				Verb: "get",
+			},
+			false,
+		},
+		{
+			"batch discovery A instances should return A labels",
+			newRequest(http.MethodPost, "/v4/:project/registry/instances/action",
+				"{\n  \"consumerId\": \"\",\n  \"instances\": [\n    {\n      \"instance\": {\n        \"serviceId\": \""+serviceIDA+"\",\n        \"instanceId\": \"\"\n      },\n      \"rev\": \"\"\n    }\n  ]\n}"),
+			&auth.ResourceScope{
+				Type: "service",
+				Labels: []map[string]string{
+					{
+						"environment": "",
+						"appId":       "TestGetAPIParseFunc",
+						"serviceName": "A",
+					},
+				},
+				Verb: "get",
+			},
+			false,
+		},
+		{
+			"batch heartbeat A instances should return A labels",
+			newRequest(http.MethodPost, "/v4/:project/registry/heartbeats",
+				"{\n  \"instances\": [\n    {\n      \"serviceId\": \""+serviceIDA+"\",\n      \"instanceId\": \"\"\n    }\n  ]\n}"),
+			&auth.ResourceScope{
+				Type: "service",
+				Labels: []map[string]string{
+					{
+						"environment": "",
+						"appId":       "TestGetAPIParseFunc",
+						"serviceName": "A",
+					},
+				},
+				Verb: "update",
+			},
+			false,
+		},
+		// not registered api
+		{
+			"govern statistics api should apply all",
+			newRequest(http.MethodGet, "/v4/:project/govern/statistics", ""),
+			&auth.ResourceScope{
+				Type: "service",
+				Verb: "get",
+			},
+			false,
+		},
+		{
+			"govern apps api should apply all",
+			newRequest(http.MethodGet, "/v4/:project/govern/apps", ""),
+			&auth.ResourceScope{
+				Type: "service",
+				Verb: "get",
+			},
+			false,
+		},
+		{
+			"account api should apply all",
+			newRequest(http.MethodGet, "/v4/accounts", ""),
+			&auth.ResourceScope{
+				Type: "account",
+				Verb: "get",
+			},
+			false,
+		},
+		{
+			"role api should apply all",
+			newRequest(http.MethodGet, "/v4/roles", ""),
+			&auth.ResourceScope{
+				Type: "role",
+				Verb: "get",
+			},
+			false,
+		},
+		{
+			"role api should apply all",
+			newRequest(http.MethodGet, "/v1/:project/gov/", ""),
+			&auth.ResourceScope{
+				Type: "governance",
+				Verb: "get",
+			},
+			false,
+		},
+	}
 
-	t.Run("govern query all api should return nil labels", func(t *testing.T) {
-		request, err := http.NewRequest(http.MethodGet, "/v4/default/govern/microservices", nil)
-		assert.NoError(t, err)
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			resource, err := buildin.GetAPIParseFunc(tt.request.URL.Path)(tt.request)
+			assert.Equal(t, tt.wantErr, err != nil)
+			assert.Equal(t, tt.wantResource == nil, resource == nil)
+			if tt.wantResource == nil {
+				return
+			}
+			assert.Equal(t, tt.wantResource.Type, resource.Type)
+			assert.Equal(t, tt.wantResource.Verb, resource.Verb)
+			assert.Equal(t, tt.wantResource.Labels == nil, resource.Labels == nil)
+			for _, labels := range tt.wantResource.Labels {
+				checkLabels(t, labels, resource)
+			}
+		})
+	}
+}
 
-		labels, err := buildin.GetAPIParseFunc("/v4/:project/govern/microservices")(request)
-		assert.NoError(t, err)
-		assert.Nil(t, labels)
-	})
+func checkLabels(t *testing.T, labels map[string]string, resource *auth.ResourceScope) {
+	var (
+		targetLabelsLength int
+		found              bool
+	)
+	for k, v := range labels {
+		for _, targetLabels := range resource.Labels {
+			targetLabelsLength = len(targetLabels)
+			found = v == targetLabels[k]
+			if found {
+				break
+			}
+		}
+		assert.True(t, found, "target label key value not matched %s=%s", k, v)
+	}
+	assert.Equal(t, len(labels), targetLabelsLength)
 }
diff --git a/server/plugin/auth/buildin/service_parser.go b/server/plugin/auth/buildin/service_parser.go
index bfc168e..c0ae548 100644
--- a/server/plugin/auth/buildin/service_parser.go
+++ b/server/plugin/auth/buildin/service_parser.go
@@ -18,62 +18,88 @@
 package buildin
 
 import (
-	"bytes"
 	"context"
 	"encoding/json"
-	"errors"
 	"fmt"
-	"io/ioutil"
-	"net/http"
-
 	"github.com/apache/servicecomb-service-center/datasource"
+	"github.com/apache/servicecomb-service-center/pkg/rest"
+	"github.com/apache/servicecomb-service-center/server/plugin/auth"
+	"github.com/apache/servicecomb-service-center/server/service/rbac"
 	"github.com/go-chassis/cari/discovery"
+	rbacmodel "github.com/go-chassis/cari/rbac"
+	"net/http"
 )
 
-var (
-	errNilRequestBody = errors.New("request body is nil")
+const (
+	LabelEnvironment = "environment"
+	LabelAppId       = "appId"
+	LabelServiceName = "serviceName"
+	QueryEnv         = "env"
 )
 
 var (
+	// Apply by service key or serviceId
+	// - /v4/:project/registry/existence?env=xxx&appId=xxx&serviceName=xxx
+	// - /v4/:project/registry/existence?serviceId=xxx&schemaId=xxx
 	APIServiceExistence = "/v4/:project/registry/existence"
+	// Method GET: apply all by optional service key
+	// Method POST or DELETE: apply by request body
 	APIServicesList     = "/v4/:project/registry/microservices"
 	APIServiceInfo      = "/v4/:project/registry/microservices/:serviceId"
-
 	APIProConDependency = "/v4/:project/registry/microservices/:providerId/consumers"
 	APIConProDependency = "/v4/:project/registry/microservices/:consumerId/providers"
-
-	APIInstancesList = "/v4/:project/registry/instances"
-	APIHeartbeats    = "/v4/:project/registry/heartbeats"
-
+	// Apply by service key
+	APIDiscovery = "/v4/:project/registry/instances"
+	// Apply by request body
+	APIBatchDiscovery = "/v4/:project/registry/instances/action"
+	// Apply by request body
+	APIHeartbeats = "/v4/:project/registry/heartbeats"
+	// Apply by optional service key
+	// - /v4/:project/govern/microservices?appId=xxx
+	// Apply all:
+	// - /v4/:project/govern/microservices?options=statistics
+	// - /v4/:project/govern/microservices/statistics
 	APIGovServicesList = "/v4/:project/govern/microservices"
 	APIGovServiceInfo  = "/v4/:project/govern/microservices/:serviceId"
 )
 
 func init() {
-	RegisterParseFunc(APIServiceInfo, serviceIdFunc)
-	RegisterParseFunc(APIGovServiceInfo, serviceIdFunc)
-	RegisterParseFunc(APIProConDependency, func(r *http.Request) ([]map[string]string, error) {
+	RegisterParseFunc(APIServiceInfo, ByServiceId)
+	RegisterParseFunc(APIGovServiceInfo, ByServiceId)
+	RegisterParseFunc(APIProConDependency, func(r *http.Request) (*auth.ResourceScope, error) {
 		return fromQueryKey(r, ":providerId")
 	})
-	RegisterParseFunc(APIConProDependency, func(r *http.Request) ([]map[string]string, error) {
+	RegisterParseFunc(APIConProDependency, func(r *http.Request) (*auth.ResourceScope, error) {
 		return fromQueryKey(r, ":consumerId")
 	})
-	RegisterParseFunc(APIInstancesList, serviceKeyFunc)
-	RegisterParseFunc(APIServiceExistence, serviceKeyFunc)
-	RegisterParseFunc(APIGovServicesList, serviceKeyFunc)
-	RegisterParseFunc(APIServicesList, serviceInfoFunc)
+	RegisterParseFunc(APIDiscovery, ByServiceKey)
+	RegisterParseFunc(APIServiceExistence, ByServiceKey)
+	RegisterParseFunc(APIGovServicesList, ApplyAll)
+	RegisterParseFunc(APIServicesList, ByRequestBody)
+	RegisterParseFunc(APIBatchDiscovery, ByDiscoveryRequestBody)
+	RegisterParseFunc(APIHeartbeats, ByHeartbeatRequestBody)
 }
 
-func serviceIdFunc(r *http.Request) ([]map[string]string, error) {
+func ByServiceId(r *http.Request) (*auth.ResourceScope, error) {
 	return fromQueryKey(r, ":serviceId")
 }
-
-func fromQueryKey(r *http.Request, queryKey string) ([]map[string]string, error) {
+func fromQueryKey(r *http.Request, queryKey string) (*auth.ResourceScope, error) {
 	ctx := r.Context()
+	apiPath, ok := ctx.Value(rest.CtxMatchPattern).(string)
+	if !ok {
+		return nil, ErrCtxMatchPatternNotFound
+	}
 	serviceId := r.URL.Query().Get(queryKey)
-	return serviceIdToLabels(ctx, serviceId)
+	labels, err := serviceIdToLabels(ctx, serviceId)
+	if err != nil {
+		return nil, err
+	}
+	return &auth.ResourceScope{
+		Type:   rbacmodel.GetResource(apiPath),
+		Labels: labels,
+		Verb:   rbac.MethodToVerbs[r.Method],
+	}, nil
 }
-
 func serviceIdToLabels(ctx context.Context, serviceId string) ([]map[string]string, error) {
 	response, err := datasource.Instance().GetService(ctx, &discovery.GetServiceRequest{ServiceId: serviceId})
 	if err != nil {
@@ -86,46 +112,71 @@ func serviceIdToLabels(ctx context.Context, serviceId string) ([]map[string]stri
 	}
 
 	return []map[string]string{{
-		"environment": service.Environment,
-		"appId":       service.AppId,
-		"serviceName": service.ServiceName,
+		LabelEnvironment: service.Environment,
+		LabelAppId:       service.AppId,
+		LabelServiceName: service.ServiceName,
 	}}, nil
 }
 
-func serviceKeyFunc(r *http.Request) ([]map[string]string, error) {
+func ByServiceKey(r *http.Request) (*auth.ResourceScope, error) {
 	query := r.URL.Query()
-	serviceId := query.Get("serviceId")
-	if len(serviceId) > 0 {
-		return serviceIdToLabels(r.Context(), serviceId)
-	}
 
-	env := query.Get("env")
-	appID := query.Get("appId")
-	serviceName := query.Get("serviceName")
+	if _, ok := query["serviceId"]; ok {
+		return fromQueryKey(r, "serviceId")
+	}
 
-	if len(env) == 0 && len(appID) == 0 && len(serviceName) == 0 {
-		return ApplyAll(r)
+	apiPath, ok := r.Context().Value(rest.CtxMatchPattern).(string)
+	if !ok {
+		return nil, ErrCtxMatchPatternNotFound
 	}
 
-	return []map[string]string{{
-		"environment": env,
-		"appId":       appID,
-		"serviceName": serviceName,
-	}}, nil
+	return &auth.ResourceScope{
+		Type: rbacmodel.GetResource(apiPath),
+		Labels: []map[string]string{{
+			LabelEnvironment: query.Get(QueryEnv),
+			LabelAppId:       query.Get(LabelAppId),
+			LabelServiceName: query.Get(LabelServiceName),
+		}},
+		Verb: rbac.MethodToVerbs[r.Method],
+	}, nil
 }
 
-func serviceInfoFunc(r *http.Request) ([]map[string]string, error) {
+func ByRequestBody(r *http.Request) (*auth.ResourceScope, error) {
 	if r.Method == http.MethodGet {
+		// get or list by query string
 		return ApplyAll(r)
 	}
+	return fromRequestBody(r)
+}
+func fromRequestBody(r *http.Request) (*auth.ResourceScope, error) {
+	apiPath, ok := r.Context().Value(rest.CtxMatchPattern).(string)
+	if !ok {
+		return nil, ErrCtxMatchPatternNotFound
+	}
+
+	var (
+		labels []map[string]string
+		err    error
+	)
 	if r.Method == http.MethodDelete {
-		return deleteServicesToLabels(r)
+		// batch delete
+		labels, err = deleteServicesToLabels(r)
+	} else {
+		// create service
+		labels, err = createServiceToLabels(r)
+	}
+	if err != nil {
+		return nil, err
 	}
-	return createServiceToLabels(r)
-}
 
+	return &auth.ResourceScope{
+		Type:   rbacmodel.GetResource(apiPath),
+		Labels: labels,
+		Verb:   rbac.MethodToVerbs[r.Method],
+	}, nil
+}
 func createServiceToLabels(r *http.Request) ([]map[string]string, error) {
-	message, err := ReadBody(r)
+	message, err := rest.ReadBody(r)
 	if err != nil {
 		return nil, err
 	}
@@ -133,7 +184,7 @@ func createServiceToLabels(r *http.Request) ([]map[string]string, error) {
 	request := &discovery.CreateServiceRequest{}
 	err = json.Unmarshal(message, request)
 	if err != nil {
-		return nil, fmt.Errorf("unmarshal CreateServiceRequest failed")
+		return nil, err
 	}
 
 	service := request.Service
@@ -142,23 +193,22 @@ func createServiceToLabels(r *http.Request) ([]map[string]string, error) {
 	}
 
 	return []map[string]string{{
-		"environment": service.Environment,
-		"appId":       service.AppId,
-		"serviceName": service.ServiceName,
+		LabelEnvironment: service.Environment,
+		LabelAppId:       service.AppId,
+		LabelServiceName: service.ServiceName,
 	}}, nil
 }
-
 func deleteServicesToLabels(r *http.Request) ([]map[string]string, error) {
-	message, err := ReadBody(r)
+	message, err := rest.ReadBody(r)
 	if err != nil {
-		return nil, fmt.Errorf("read request body failed")
+		return nil, err
 	}
 
 	request := &discovery.DelServicesRequest{}
 
 	err = json.Unmarshal(message, request)
 	if err != nil {
-		return nil, fmt.Errorf("unmarshal DelServicesRequest failed")
+		return nil, err
 	}
 
 	ctx := r.Context()
@@ -173,15 +223,72 @@ func deleteServicesToLabels(r *http.Request) ([]map[string]string, error) {
 	return labels, nil
 }
 
-func ReadBody(r *http.Request) ([]byte, error) {
-	if r.Body == nil {
-		return nil, errNilRequestBody
+func ByDiscoveryRequestBody(r *http.Request) (*auth.ResourceScope, error) {
+	apiPath, ok := r.Context().Value(rest.CtxMatchPattern).(string)
+	if !ok {
+		return nil, ErrCtxMatchPatternNotFound
 	}
 
-	data, err := ioutil.ReadAll(r.Body)
+	message, err := rest.ReadBody(r)
 	if err != nil {
 		return nil, err
 	}
-	r.Body = ioutil.NopCloser(bytes.NewReader(data))
-	return data, nil
+
+	request := &discovery.BatchFindInstancesRequest{}
+
+	err = json.Unmarshal(message, request)
+	if err != nil {
+		return nil, err
+	}
+
+	ctx := r.Context()
+	var labels []map[string]string
+	for _, it := range request.Instances {
+		ls, err := serviceIdToLabels(ctx, it.Instance.ServiceId)
+		if err != nil {
+			return nil, err
+		}
+		labels = append(labels, ls...)
+	}
+
+	return &auth.ResourceScope{
+		Type:   rbacmodel.GetResource(apiPath),
+		Labels: labels,
+		Verb:   "get",
+	}, nil
+}
+
+func ByHeartbeatRequestBody(r *http.Request) (*auth.ResourceScope, error) {
+	apiPath, ok := r.Context().Value(rest.CtxMatchPattern).(string)
+	if !ok {
+		return nil, ErrCtxMatchPatternNotFound
+	}
+
+	message, err := rest.ReadBody(r)
+	if err != nil {
+		return nil, err
+	}
+
+	request := &discovery.HeartbeatSetRequest{}
+
+	err = json.Unmarshal(message, request)
+	if err != nil {
+		return nil, err
+	}
+
+	ctx := r.Context()
+	var labels []map[string]string
+	for _, instance := range request.Instances {
+		ls, err := serviceIdToLabels(ctx, instance.ServiceId)
+		if err != nil {
+			return nil, err
+		}
+		labels = append(labels, ls...)
+	}
+
+	return &auth.ResourceScope{
+		Type:   rbacmodel.GetResource(apiPath),
+		Labels: labels,
+		Verb:   "update",
+	}, nil
 }
diff --git a/server/plugin/auth/types.go b/server/plugin/auth/types.go
index 035796c..0dbba46 100644
--- a/server/plugin/auth/types.go
+++ b/server/plugin/auth/types.go
@@ -19,7 +19,9 @@ package auth
 
 // ResourceScope is the resource scope parsed from request
 type ResourceScope struct {
-	Type   string
+	Type string
+	// Labels is a map used to filter resource permissions during pre verification.
+	// If a key of permission set is missing in the Labels, pre verification will pass this key
 	Labels []map[string]string
 	// Verb is the apply resource action, e.g. "get", "create"
 	Verb string
diff --git a/server/response/response.go b/server/response/response.go
index 20c52a4..74515a0 100644
--- a/server/response/response.go
+++ b/server/response/response.go
@@ -24,7 +24,6 @@ import (
 
 func init() {
 	RegisterFilter("/v4/:project/registry/microservices", MicroserviceListFilter)
-	//response.RegisterFilter("/v4/:project/registry/instances", )
 	RegisterFilter("/v4/:project/registry/microservices/:providerId/consumers", ProvidersListFilter)
 	RegisterFilter("/v4/:project/registry/microservices/:consumerId/providers", ConsumersListFilter)
 	// control panel apis
diff --git a/server/service/rbac/rbac.go b/server/service/rbac/rbac.go
index f7e5aa4..76c7258 100644
--- a/server/service/rbac/rbac.go
+++ b/server/service/rbac/rbac.go
@@ -56,7 +56,7 @@ func Init() {
 		log.Info("rbac is disabled")
 		return
 	}
-	initResourceMap()
+	InitResourceMap()
 	err := authr.Init()
 	if err != nil {
 		log.Fatal("can not enable auth module", err)
diff --git a/server/service/rbac/resource.go b/server/service/rbac/resource.go
index edc5beb..71917a2 100644
--- a/server/service/rbac/resource.go
+++ b/server/service/rbac/resource.go
@@ -66,7 +66,7 @@ var (
 	APIServiceSchema = "/v4/:project/registry/microservices/:serviceId/schemas"
 )
 
-func initResourceMap() {
+func InitResourceMap() {
 	rbac.PartialMapResource(APIAccountList, ResourceAccount)
 
 	rbac.PartialMapResource(APIRoleList, ResourceRole)