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

[servicecomb-service-center] branch master updated: add rbac logic (#1014)

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

tianxiaoliang 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 f8cbf0f  add rbac logic (#1014)
f8cbf0f is described below

commit f8cbf0fe6d1b93eddc7e2c5a1152880b213c7d8f
Author: humingcheng <hu...@163.com>
AuthorDate: Wed May 26 23:09:57 2021 +0800

    add rbac logic (#1014)
---
 go.mod                                |   2 +-
 go.sum                                |   2 +
 server/handler/auth/auth.go           |   1 +
 server/plugin/auth/buildin/buildin.go | 117 ++++++++++++++-------
 server/resource/v4/auth_resource.go   |   4 +-
 server/service/rbac/decision.go       | 114 ++++++++++++++++----
 server/service/rbac/decision_test.go  | 191 +++++++++++++++++++++++++++-------
 server/service/rbac/resource.go       |   4 +-
 8 files changed, 335 insertions(+), 100 deletions(-)

diff --git a/go.mod b/go.mod
index 1d807a3..2d9ae3f 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/elithrar/simple-scrypt v1.3.0
 	github.com/ghodss/yaml v1.0.0
-	github.com/go-chassis/cari v0.4.1-0.20210525100133-33b23132d6f0
+	github.com/go-chassis/cari v0.4.1-0.20210526130437-0685eaf85b3a
 	github.com/go-chassis/foundation v0.3.1-0.20210513015331-b54416b66bcd
 	github.com/go-chassis/go-archaius v1.5.1
 	github.com/go-chassis/go-chassis/v2 v2.1.2-0.20210310004133-c9bc42149a18
diff --git a/go.sum b/go.sum
index ddcf679..dc7fe01 100644
--- a/go.sum
+++ b/go.sum
@@ -272,6 +272,8 @@ github.com/go-chassis/cari v0.4.1-0.20210522033018-d228d49d7d61 h1:gVgeg1lmX1mpr
 github.com/go-chassis/cari v0.4.1-0.20210522033018-d228d49d7d61/go.mod h1:av/19fqwEP4eOC8unL/z67AAbFDwXUCko6SKa4Avrd8=
 github.com/go-chassis/cari v0.4.1-0.20210525100133-33b23132d6f0 h1:cxI44jhmc1yzyDm6MQ4oYutZroHlaPkpJLrmsLQJybY=
 github.com/go-chassis/cari v0.4.1-0.20210525100133-33b23132d6f0/go.mod h1:av/19fqwEP4eOC8unL/z67AAbFDwXUCko6SKa4Avrd8=
+github.com/go-chassis/cari v0.4.1-0.20210526130437-0685eaf85b3a h1:YQH/VGLB9eveB39JH818nTD9HObV5tU7C+ALieupaX0=
+github.com/go-chassis/cari v0.4.1-0.20210526130437-0685eaf85b3a/go.mod h1:av/19fqwEP4eOC8unL/z67AAbFDwXUCko6SKa4Avrd8=
 github.com/go-chassis/foundation v0.2.2-0.20201210043510-9f6d3de40234/go.mod h1:2PjwqpVwYEVaAldl5A58a08viH8p27pNeYaiE3ZxOBA=
 github.com/go-chassis/foundation v0.2.2/go.mod h1:2PjwqpVwYEVaAldl5A58a08viH8p27pNeYaiE3ZxOBA=
 github.com/go-chassis/foundation v0.3.0/go.mod h1:2PjwqpVwYEVaAldl5A58a08viH8p27pNeYaiE3ZxOBA=
diff --git a/server/handler/auth/auth.go b/server/handler/auth/auth.go
index f1b1053..034acc4 100644
--- a/server/handler/auth/auth.go
+++ b/server/handler/auth/auth.go
@@ -32,6 +32,7 @@ import (
 const (
 	CtxResourceLabels util.CtxKey = "_resource_labels"
 	CtxResourceScopes util.CtxKey = "_resource_scopes"
+	CtxRequestClaims  util.CtxKey = "_request_claims"
 )
 
 type Handler struct {
diff --git a/server/plugin/auth/buildin/buildin.go b/server/plugin/auth/buildin/buildin.go
index 69001e2..3117e30 100644
--- a/server/plugin/auth/buildin/buildin.go
+++ b/server/plugin/auth/buildin/buildin.go
@@ -30,6 +30,7 @@ import (
 	errorsEx "github.com/apache/servicecomb-service-center/pkg/errors"
 	"github.com/apache/servicecomb-service-center/pkg/log"
 	"github.com/apache/servicecomb-service-center/pkg/rest"
+	authHandler "github.com/apache/servicecomb-service-center/server/handler/auth"
 	"github.com/apache/servicecomb-service-center/server/plugin/auth"
 	rbacsvc "github.com/apache/servicecomb-service-center/server/service/rbac"
 	"github.com/go-chassis/go-chassis/v2/security/authr"
@@ -52,73 +53,102 @@ func (ba *TokenAuthenticator) Identify(req *http.Request) error {
 		return nil
 	}
 	pattern, ok := req.Context().Value(rest.CtxMatchPattern).(string)
-	if ok && !mustAuth(pattern) {
-		return nil
-	}
-	v := req.Header.Get(restful.HeaderAuth)
-	if v == "" {
-		return rbac.ErrNoHeader
+	if !ok {
+		pattern = req.URL.Path
+		log.Warn("can not find api pattern")
 	}
-	s := strings.Split(v, " ")
-	if len(s) != 2 {
-		return rbac.ErrInvalidHeader
+
+	if !mustAuth(pattern) {
+		return nil
 	}
-	to := s[1]
 
-	claims, err := authr.Authenticate(req.Context(), to)
+	claims, err := ba.VerifyToken(req)
 	if err != nil {
-		log.Errorf(err, "authenticate request failed, %s %s", req.Method, req.RequestURI)
+		log.Errorf(err, "verify request token failed, %s %s", req.Method, req.RequestURI)
 		return err
 	}
+
 	m, ok := claims.(map[string]interface{})
 	if !ok {
 		log.Error("claims convert failed", rbac.ErrConvertErr)
 		return rbac.ErrConvertErr
 	}
-
-	roleList, err := rbac.GetRolesList(m)
+	account, err := rbac.GetAccount(m)
 	if err != nil {
-		log.Error("get role list failed", err)
+		log.Error("get account  failed", err)
 		return err
 	}
+	util.SetRequestContext(req, authHandler.CtxRequestClaims, m)
+	// user can change self password
+	if isChangeSelfPassword(pattern, account, req) {
+		return nil
+	}
 
-	var apiPattern string
-	a := req.Context().Value(rest.CtxMatchPattern)
-	if a == nil { //handle exception
-		apiPattern = req.URL.Path
-		log.Warn("can not find api pattern")
-	} else {
-		apiPattern = a.(string)
+	if len(account.Roles) == 0 {
+		log.Error("no role found in token", nil)
+		return errors.New(errorsEx.MsgNoPerm)
 	}
 
 	project := req.URL.Query().Get(":project")
-	verbs := rbacsvc.MethodToVerbs[req.Method]
-	err = checkPerm(roleList, project, apiPattern, verbs)
+	allow, matchedLabels, err := checkPerm(account.Roles, project, req, pattern, req.Method)
 	if err != nil {
 		return err
 	}
-	req2 := req.WithContext(rbac.NewContext(req.Context(), m))
-	*req = *req2
+	if !allow {
+		return errors.New(errorsEx.MsgNoPerm)
+	}
+
+	util.SetRequestContext(req, authHandler.CtxResourceLabels, matchedLabels)
 	return nil
 }
 
-//this method decouple business code and perm checks
-func checkPerm(roleList []string, project, apiPattern, verbs string) error {
-	resource := rbac.GetResource(apiPattern)
-	if resource == "" {
-		//fast fail, no need to access role storage
-		return errors.New(errorsEx.MsgNoPerm)
+func isChangeSelfPassword(pattern string, a *rbac.Account, req *http.Request) bool {
+	if pattern != rbacsvc.APIAccountPassword {
+		return false
 	}
-	//TODO add verbs,project
-	allow, err := rbacsvc.Allow(context.TODO(), roleList, project, resource, verbs)
-	if err != nil {
-		log.Error("", err)
-		return errors.New(errorsEx.MsgRolePerm)
+	changerName := a.Name
+	targetName := req.URL.Query().Get(":name")
+	return changerName == targetName
+}
+
+func filterRoles(roleList []string) (hasAdmin bool, normalRoles []string) {
+	for _, r := range roleList {
+		if r == rbac.RoleAdmin {
+			hasAdmin = true
+			return
+		}
+		normalRoles = append(normalRoles, r)
 	}
-	if !allow {
-		return errors.New(errorsEx.MsgNoPerm)
+	return
+}
+
+func (ba *TokenAuthenticator) VerifyToken(req *http.Request) (interface{}, error) {
+	v := req.Header.Get(restful.HeaderAuth)
+	if v == "" {
+		return nil, rbac.ErrNoHeader
 	}
-	return nil
+	s := strings.Split(v, " ")
+	if len(s) != 2 {
+		return nil, rbac.ErrInvalidHeader
+	}
+	to := s[1]
+
+	return authr.Authenticate(req.Context(), to)
+}
+
+//this method decouple business code and perm checks
+func checkPerm(roleList []string, project string, req *http.Request, apiPattern, method string) (bool, []map[string]string, error) {
+	hasAdmin, normalRoles := filterRoles(roleList)
+	if hasAdmin {
+		return true, nil, nil
+	}
+	//todo fast check for dev role
+	targetResource, ok := req.Context().Value(authHandler.CtxResourceScopes).(*auth.ResourceScope)
+	if !ok || targetResource == nil {
+		return false, nil, errors.New("no valid resouce scope")
+	}
+	//TODO add project
+	return rbacsvc.Allow(context.TODO(), project, normalRoles, targetResource)
 }
 
 func mustAuth(pattern string) bool {
@@ -134,3 +164,10 @@ func (ba *TokenAuthenticator) ResourceScopes(r *http.Request) *auth.ResourceScop
 	}
 	return FromRequest(r)
 }
+func AccountFromContext(ctx context.Context) (*rbac.Account, error) {
+	m, ok := ctx.Value(authHandler.CtxRequestClaims).(map[string]interface{})
+	if !ok {
+		return nil, errors.New("no claims from request context")
+	}
+	return rbac.GetAccount(m)
+}
diff --git a/server/resource/v4/auth_resource.go b/server/resource/v4/auth_resource.go
index 19ede1d..6eda172 100644
--- a/server/resource/v4/auth_resource.go
+++ b/server/resource/v4/auth_resource.go
@@ -29,6 +29,7 @@ import (
 	"github.com/apache/servicecomb-service-center/pkg/log"
 	"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/buildin"
 	rbacsvc "github.com/apache/servicecomb-service-center/server/service/rbac"
 	"github.com/apache/servicecomb-service-center/server/service/rbac/dao"
 	"github.com/apache/servicecomb-service-center/server/service/validator"
@@ -173,7 +174,8 @@ func (ar *AuthResource) ChangePassword(w http.ResponseWriter, req *http.Request)
 		rest.WriteError(w, discovery.ErrInvalidParams, err.Error())
 		return
 	}
-	changer, err := rbac.AccountFromContext(req.Context())
+
+	changer, err := buildin.AccountFromContext(req.Context())
 	if err != nil {
 		rest.WriteError(w, discovery.ErrInternal, "can not parse account info")
 		return
diff --git a/server/service/rbac/decision.go b/server/service/rbac/decision.go
index ddf0650..545f99b 100644
--- a/server/service/rbac/decision.go
+++ b/server/service/rbac/decision.go
@@ -19,25 +19,74 @@ package rbac
 
 import (
 	"context"
-
 	"github.com/go-chassis/cari/rbac"
 
 	"github.com/apache/servicecomb-service-center/datasource"
 	"github.com/apache/servicecomb-service-center/pkg/log"
+	"github.com/apache/servicecomb-service-center/server/plugin/auth"
 )
 
-func Allow(ctx context.Context, roleList []string, project, resource, verbs string) (bool, error) {
+// return: allow, matched labels(empty if no label defined), error
+func Allow(ctx context.Context, project string, roleList []string,
+	targetResouce *auth.ResourceScope) (bool, []map[string]string, error) {
 	//TODO check project
-	if ableToOperateResource(roleList, "admin") {
-		return true, nil
+	allPerms, err := getPermsByRoles(ctx, roleList)
+	if err != nil {
+		log.Error("get role list errors", err)
+		return false, nil, err
+	}
+	if len(allPerms) == 0 {
+		log.Warn("role list has no any permissions")
+		return false, nil, nil
+	}
+	allow, labelList := GetLabel(allPerms, targetResouce.Type, targetResouce.Verb)
+	if !allow {
+		return false, nil, nil
+	}
+	// allow, but no label found, means we can ignore the labels
+	if len(labelList) == 0 {
+		return true, nil, nil
+	}
+	// target resource needs no label, return without filter
+	if len(targetResouce.Labels) == 0 {
+		return true, labelList, nil
+	}
+	// allow, and labels found, filter the labels
+	filteredLabelList := FilterLabel(targetResouce.Labels, labelList)
+	// target resource label matches no label in permission, means not allow
+	if len(filteredLabelList) == 0 {
+		return false, nil, nil
+	}
+	return true, filteredLabelList, nil
+}
+
+func FilterLabel(targetResourceLabel []map[string]string, permLabelList []map[string]string) []map[string]string {
+	l := make([]map[string]string, 0)
+	for _, resourceLabel := range targetResourceLabel {
+		for _, label := range permLabelList {
+			if LabelMatched(resourceLabel, label) {
+				l = append(l, label)
+			}
+		}
 	}
-	// allPerms combines the roleList permission
+	return l
+}
+
+func LabelMatched(targetResourceLabel map[string]string, permLabel map[string]string) bool {
+	for k, v := range permLabel {
+		if vv := targetResourceLabel[k]; vv != v {
+			return false
+		}
+	}
+	return true
+}
+
+func getPermsByRoles(ctx context.Context, roleList []string) ([]*rbac.Permission, error) {
 	var allPerms = make([]*rbac.Permission, 0)
 	for i := 0; i < len(roleList); i++ {
 		r, err := datasource.Instance().GetRole(ctx, roleList[i])
 		if err != nil {
-			log.Error("get role list errors", err)
-			return false, err
+			return nil, err
 		}
 		if r == nil {
 			log.Warnf("role [%s] has no any permissions", roleList[i])
@@ -45,22 +94,38 @@ func Allow(ctx context.Context, roleList []string, project, resource, verbs stri
 		}
 		allPerms = append(allPerms, r.Perms...)
 	}
+	return allPerms, nil
+}
 
-	if len(allPerms) == 0 {
-		log.Warn("role list has no any permissions")
-		return false, nil
-	}
-	for i := 0; i < len(allPerms); i++ {
-		if ableToAccessResource(allPerms[i].Resources, resource) && ableToOperateResource(allPerms[i].Verbs, verbs) {
+// GetLabel checks if the perms have permission to operate the resource(ignore label),
+// if one perm have the permission, add it's label to the result.
+func GetLabel(perms []*rbac.Permission, targetResource, verb string) (allow bool, labelList []map[string]string) {
+	for _, perm := range perms {
+		a, l := GetLabelFromSinglePerm(perm, targetResource, verb)
+		if !a {
+			continue
+		}
+		allow = true
+		// allow and has no label, return fast
+		if len(l) == 0 {
 			return true, nil
 		}
+		labelList = append(labelList, l...)
 	}
+	return
+}
 
-	log.Warn("role is not allowed to operate resource")
-	return false, nil
+// GetLabel checks if the perm have permission to operate the resource(ignore label),
+// if the perm have the permission, return it's label.
+func GetLabelFromSinglePerm(perm *rbac.Permission, targetResource, verb string) (allow bool, labelList []map[string]string) {
+	if !allowVerb(perm.Verbs, verb) {
+		return false, nil
+	}
+
+	return getResourceLabel(perm.Resources, targetResource)
 }
 
-func ableToOperateResource(haystack []string, needle string) bool {
+func allowVerb(haystack []string, needle string) bool {
 	for _, e := range haystack {
 		if e == "*" || e == needle {
 			return true
@@ -69,11 +134,18 @@ func ableToOperateResource(haystack []string, needle string) bool {
 	return false
 }
 
-func ableToAccessResource(haystack []*rbac.Resource, needle string) bool {
-	for _, e := range haystack {
-		if e.Type == needle {
-			return true
+func getResourceLabel(resources []*rbac.Resource, needle string) (allow bool, labelList []map[string]string) {
+	for _, resource := range resources {
+		// filter the same resource
+		if resource.Type != needle {
+			continue
+		}
+		// has no label, return fast
+		if len(resource.Labels) == 0 {
+			return true, nil
 		}
+		labelList = append(labelList, resource.Labels)
+		allow = true
 	}
-	return false
+	return
 }
diff --git a/server/service/rbac/decision_test.go b/server/service/rbac/decision_test.go
index acc9cf4..20cbdd1 100644
--- a/server/service/rbac/decision_test.go
+++ b/server/service/rbac/decision_test.go
@@ -18,55 +18,174 @@
 package rbac_test
 
 import (
-	"context"
-	"io/ioutil"
 	"testing"
 
-	"github.com/go-chassis/go-archaius"
-	"github.com/go-chassis/go-chassis/v2/security/secret"
+	"github.com/go-chassis/cari/rbac"
 	"github.com/stretchr/testify/assert"
 
-	"github.com/apache/servicecomb-service-center/server/service/rbac"
-	"github.com/apache/servicecomb-service-center/server/service/rbac/dao"
+	rbacsvc "github.com/apache/servicecomb-service-center/server/service/rbac"
 )
 
-func TestAllow(t *testing.T) {
-	err := archaius.Init(archaius.WithMemorySource(), archaius.WithENVSource())
-	assert.NoError(t, err)
+func TestGetLabel(t *testing.T) {
+	perms := []*rbac.Permission{
+		&rbac.Permission{
+			Resources: []*rbac.Resource{
+				{
+					Type:   rbacsvc.ResourceAccount,
+					Labels: map[string]string{"environment": "production"},
+				},
+				{
+					Type:   rbacsvc.ResourceService,
+					Labels: map[string]string{"serviceName": "service-center"},
+				},
+			},
+			Verbs: []string{"get"},
+		},
+		&rbac.Permission{
+			Resources: []*rbac.Resource{
+				{
+					Type:   rbacsvc.ResourceService,
+					Labels: map[string]string{"appId": "default"},
+				},
+			},
+			Verbs: []string{"*"},
+		},
+		&rbac.Permission{
+			Resources: []*rbac.Resource{
+				{
+					Type: rbacsvc.ResourceService,
+				},
+			},
+			Verbs: []string{"delete"},
+		},
+	}
+	t.Run("resource and verb matched, should allow", func(t *testing.T) {
+		allow, labelList := rbacsvc.GetLabel(perms, rbacsvc.ResourceService, "create")
+		assert.True(t, allow)
+		assert.Equal(t, 1, len(labelList))
+	})
 
-	pri, pub, err := secret.GenRSAKeyPair(4096)
-	assert.NoError(t, err)
+	t.Run("nums of resource matched, should allow and combine their labels", func(t *testing.T) {
+		allow, labelList := rbacsvc.GetLabel(perms, rbacsvc.ResourceService, "get")
+		assert.True(t, allow)
+		assert.Equal(t, 2, len(labelList))
+	})
 
-	b, err := secret.RSAPrivate2Bytes(pri)
-	assert.NoError(t, err)
-	ioutil.WriteFile("./private.key", b, 0600)
-	b, err = secret.RSAPublicKey2Bytes(pub)
-	err = ioutil.WriteFile("./rbac.pub", b, 0600)
-	assert.NoError(t, err)
+	t.Run("nums of resource matched, one of them has no label, should allow and no label", func(t *testing.T) {
+		allow, labelList := rbacsvc.GetLabel(perms, rbacsvc.ResourceService, "delete")
+		assert.True(t, allow)
+		assert.Equal(t, 0, len(labelList))
+	})
+	t.Run("resource not matched, should not allow", func(t *testing.T) {
+		allow, labelList := rbacsvc.GetLabel(perms, rbacsvc.ResourceRole, "delete")
+		assert.False(t, allow)
+		assert.Equal(t, 0, len(labelList))
+	})
+	t.Run("Verb not matched, should not allow", func(t *testing.T) {
+		allow, labelList := rbacsvc.GetLabel(perms, rbacsvc.ResourceAccount, "delete")
+		assert.False(t, allow)
+		assert.Equal(t, 0, len(labelList))
+	})
+}
 
-	archaius.Set(rbac.InitPassword, "Complicated_password1")
+func TestGetLabelFromSinglePerm(t *testing.T) {
+	t.Run("resource and verb match, should allow", func(t *testing.T) {
+		perms := &rbac.Permission{
+			Resources: []*rbac.Resource{
+				{
+					Type:   rbacsvc.ResourceAccount,
+					Labels: map[string]string{"environment": "production"},
+				},
+			},
+			Verbs: []string{"*"},
+		}
+		allow, labelList := rbacsvc.GetLabelFromSinglePerm(perms, rbacsvc.ResourceAccount, "create")
+		assert.True(t, allow)
+		assert.Equal(t, 1, len(labelList))
+		assert.Equal(t, "production", labelList[0]["environment"])
+	})
 
-	dao.DeleteAccount(context.Background(), "root")
-	dao.DeleteAccount(context.Background(), "a")
-	dao.DeleteAccount(context.Background(), "b")
+	t.Run("resource not match, should no allow", func(t *testing.T) {
+		perms := &rbac.Permission{
+			Resources: []*rbac.Resource{
+				{
+					Type:   rbacsvc.ResourceAccount,
+					Labels: map[string]string{"environment": "production"},
+				},
+			},
+			Verbs: []string{"*"},
+		}
+		allow, labelList := rbacsvc.GetLabelFromSinglePerm(perms, rbacsvc.ResourceService, "create")
+		assert.False(t, allow)
+		assert.Equal(t, 0, len(labelList))
+	})
 
-	rbac.Init()
-	a, err := dao.GetAccount(context.Background(), "root")
-	assert.NoError(t, err)
-	assert.Equal(t, "root", a.Name)
+	t.Run("verb not match, should no allow", func(t *testing.T) {
+		perms := &rbac.Permission{
+			Resources: []*rbac.Resource{
+				{
+					Type:   rbacsvc.ResourceAccount,
+					Labels: map[string]string{"environment": "production"},
+				},
+			},
+			Verbs: []string{"get"},
+		}
+		allow, labelList := rbacsvc.GetLabelFromSinglePerm(perms, rbacsvc.ResourceAccount, "create")
+		assert.False(t, allow)
+		assert.Equal(t, 0, len(labelList))
+	})
+}
 
-	t.Run("admin can operate any resource", func(t *testing.T) {
-		ok, _ := rbac.Allow(context.TODO(), []string{"admin"}, "default", "account", "create")
-		assert.True(t, ok)
-		ok, _ = rbac.Allow(context.TODO(), []string{"admin"}, "default", "service", "create")
-		assert.True(t, ok)
+func TestLabelMatched(t *testing.T) {
+	targetResourceLabel := map[string]string{
+		"environment": "production",
+		"appId":       "default",
+	}
+	t.Run("value not match, should not match", func(t *testing.T) {
+		permResourceLabel := map[string]string{
+			"environment": "testing",
+		}
+		assert.False(t, rbacsvc.LabelMatched(targetResourceLabel, permResourceLabel))
 	})
-	t.Run("developer can not operate account", func(t *testing.T) {
-		ok, _ := rbac.Allow(context.TODO(), []string{"developer"}, "default", "account", "create")
-		assert.False(t, ok)
+	t.Run("key not match, should not match", func(t *testing.T) {
+		permResourceLabel := map[string]string{
+			"serviceName": "default",
+		}
+		assert.False(t, rbacsvc.LabelMatched(targetResourceLabel, permResourceLabel))
 	})
-	t.Run("developer can operate service", func(t *testing.T) {
-		ok, _ := rbac.Allow(context.TODO(), []string{"developer"}, "default", "service", "create")
-		assert.True(t, ok)
+	t.Run("target resource label matches no permission resource label, should not match", func(t *testing.T) {
+		permResourceLabel := map[string]string{
+			"version": "1.0.0",
+		}
+		assert.False(t, rbacsvc.LabelMatched(targetResourceLabel, permResourceLabel))
 	})
+	t.Run("target resource label matches part permission resource label, should not match", func(t *testing.T) {
+		permResourceLabel := map[string]string{
+			"version":     "1.0.0",
+			"environment": "production",
+		}
+		assert.False(t, rbacsvc.LabelMatched(targetResourceLabel, permResourceLabel))
+	})
+	t.Run("target resource label matches  permission resource label, should not match", func(t *testing.T) {
+		permResourceLabel := map[string]string{
+			"environment": "production",
+		}
+		assert.True(t, rbacsvc.LabelMatched(targetResourceLabel, permResourceLabel))
+	})
+}
+func TestFilterLabel(t *testing.T) {
+	targetResourceLabel := []map[string]string{
+		{"environment": "production", "appId": "default"},
+		{"serviceName": "service-center"},
+	}
+	permResourceLabel := []map[string]string{
+		{"environment": "production", "appId": "default"},
+		{"appId": "default"},
+		{"environment": "production", "serviceName": "service-center"},
+		{"serviceName": "service-center", "version": "1.0.0"},
+		{"serviceName": "service-center"},
+		{"environment": "testing"},
+	}
+	l := rbacsvc.FilterLabel(targetResourceLabel, permResourceLabel)
+	assert.Equal(t, 3, len(l))
 }
diff --git a/server/service/rbac/resource.go b/server/service/rbac/resource.go
index 8043ffc..edc5beb 100644
--- a/server/service/rbac/resource.go
+++ b/server/service/rbac/resource.go
@@ -26,7 +26,7 @@ const (
 	ResourceRole    = "role"
 	ResourceService = "service"
 	ResourceGovern  = "governance"
-	ResourceSchema  = "schema"
+	ResourceSchema  = "service/schema"
 	ResourceOps     = "ops"
 )
 
@@ -37,6 +37,8 @@ var (
 
 	APIRoleList = "/v4/roles"
 
+	APIAccountPassword = "/v4/accounts/:name/password"
+
 	APIOps = "/v4/:project/admin"
 
 	APIGov = "/v1/:project/gov/"