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 2020/07/16 06:47:38 UTC

[servicecomb-service-center] branch master updated: delete account, list account (#668)

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 a3a5ebd  delete account,list account (#668)
a3a5ebd is described below

commit a3a5ebdff1d2b1b0eb3d9c3af0e72b0d363b8815
Author: Shawn <xi...@gmail.com>
AuthorDate: Thu Jul 16 14:47:23 2020 +0800

    delete account,list account (#668)
---
 pkg/errors/text.go                                 |  9 ++-
 pkg/rbacframe/account.go                           |  7 ++
 .../rbac/dao => pkg/rbacframe}/resource_dao.go     | 25 ++++---
 server/plugin/auth/buildin/buidlin_test.go         | 21 +++++-
 server/plugin/auth/buildin/buildin.go              | 37 +++++++---
 server/rest/controller/v4/auth_resource.go         | 55 +++++++++++++-
 server/rest/controller/v4/auth_resource_test.go    | 86 ++++++++++++++++++++++
 server/service/kv/store.go                         | 10 +++
 server/service/kv/store_test.go                    | 11 +++
 server/service/rbac/dao/account_dao.go             | 29 +++++++-
 server/service/rbac/rbac.go                        | 11 +++
 server/service/rbac/rbca_test.go                   |  5 ++
 12 files changed, 272 insertions(+), 34 deletions(-)

diff --git a/pkg/errors/text.go b/pkg/errors/text.go
index a9c3e7b..998e17e 100644
--- a/pkg/errors/text.go
+++ b/pkg/errors/text.go
@@ -18,9 +18,10 @@
 package errors
 
 const (
-	ErrMsgJSON = "json is invalid"
+	MsgJSON = "json is invalid"
 
-	ErrMsgCreateAccount = "create account failed"
-	ErrMsgRolePerm      = "check role permissions failed"
-	ErrMsgNoPerm        = "no permission to operate"
+	MsgOperateAccountFailed = "operate account failed"
+	MsgGetAccountFailed     = "get account failed"
+	MsgRolePerm             = "check role permissions failed"
+	MsgNoPerm               = "no permission to operate"
 )
diff --git a/pkg/rbacframe/account.go b/pkg/rbacframe/account.go
index 4b32c4f..00cd133 100644
--- a/pkg/rbacframe/account.go
+++ b/pkg/rbacframe/account.go
@@ -21,12 +21,19 @@ const (
 	RoleAdmin = "admin"
 )
 
+type AccountResponse struct {
+	Total    int64      `json:"total"`
+	Accounts []*Account `json:"data,omitempty"`
+}
+
 type Account struct {
+	ID              string `json:"id,omitempty"`
 	Name            string `json:"name,omitempty"`
 	Password        string `json:"password,omitempty"`
 	Role            string `json:"role,omitempty"`
 	TokenExpiryTime string `json:"tokenExpiryTime,omitempty"`
 	CurrentPassword string `json:"currentPassword,omitempty"`
+	Status          string `json:"status,omitempty"`
 }
 
 type Token struct {
diff --git a/server/service/rbac/dao/resource_dao.go b/pkg/rbacframe/resource_dao.go
similarity index 63%
rename from server/service/rbac/dao/resource_dao.go
rename to pkg/rbacframe/resource_dao.go
index f0b01da..1e39350 100644
--- a/server/service/rbac/dao/resource_dao.go
+++ b/pkg/rbacframe/resource_dao.go
@@ -15,22 +15,23 @@
  * limitations under the License.
  */
 
-package dao
+package rbacframe
 
-import "context"
+//as a user, he only understand resource of this system,
+//but to decouple authorization code from business code,
+//a middleware should handle all the authorization logic, and this middleware only understand rest API,
+//a resource mapping helps to do it.
+var resourceMap = map[string]string{}
 
-//TODO save to etcd
-//TODO now simply write dead code "*" to map all other API except account and role to service, should define resource for every API in future
-var resourceMap = map[string]string{
-	"/v4/account": "account",
-	"/v4/role":    "account",
-	"*":           "service",
-}
-
-func GetResource(ctx context.Context, API string) string {
-	r, ok := resourceMap[API]
+func GetResource(api string) string {
+	r, ok := resourceMap[api]
 	if !ok {
 		return resourceMap["*"]
 	}
 	return r
 }
+
+//MapResource save the mapping from api to resource
+func MapResource(api, resource string) {
+	resourceMap[api] = resource
+}
diff --git a/server/plugin/auth/buildin/buidlin_test.go b/server/plugin/auth/buildin/buidlin_test.go
index f6953da..39f09a3 100644
--- a/server/plugin/auth/buildin/buidlin_test.go
+++ b/server/plugin/auth/buildin/buidlin_test.go
@@ -98,7 +98,7 @@ func TestTokenAuthenticator_Identify(t *testing.T) {
 		t.Log(err)
 		assert.Error(t, err)
 	})
-	t.Run("valid admin token, should be able to operate account", func(t *testing.T) {
+	t.Run("valid admin token, should be able to get account", func(t *testing.T) {
 		r := httptest.NewRequest(http.MethodGet, "/v4/account", nil)
 		to, err := authr.Login(context.TODO(), "root", "Complicated_password1")
 		assert.NoError(t, err)
@@ -106,7 +106,7 @@ func TestTokenAuthenticator_Identify(t *testing.T) {
 		err = ta.Identify(r)
 		assert.NoError(t, err)
 	})
-	t.Run("valid normal token, should no be able to operate account", func(t *testing.T) {
+	t.Run("valid normal token, should no be able to get account", func(t *testing.T) {
 		err := dao.CreateAccount(context.TODO(), &rbacframe.Account{Name: "non-admin", Password: "Complicated_password1", Role: "developer"})
 		assert.NoError(t, err)
 		r := httptest.NewRequest(http.MethodGet, "/v4/account", nil)
@@ -117,4 +117,21 @@ func TestTokenAuthenticator_Identify(t *testing.T) {
 		t.Log(err)
 		assert.Error(t, err)
 	})
+	t.Run("valid normal token, should no be able to delete account", func(t *testing.T) {
+		r := httptest.NewRequest(http.MethodDelete, "/v4/account", nil)
+		to, err := authr.Login(context.TODO(), "non-admin", "Complicated_password1")
+		assert.NoError(t, err)
+		r.Header.Set(restful.HeaderAuth, "Bear "+to)
+		err = ta.Identify(r)
+		t.Log(err)
+		assert.Error(t, err)
+	})
+	t.Run("valid admin token, should be able to delete account", func(t *testing.T) {
+		r := httptest.NewRequest(http.MethodDelete, "/v4/account", nil)
+		to, err := authr.Login(context.TODO(), "root", "Complicated_password1")
+		assert.NoError(t, err)
+		r.Header.Set(restful.HeaderAuth, "Bear "+to)
+		err = ta.Identify(r)
+		assert.NoError(t, err)
+	})
 }
diff --git a/server/plugin/auth/buildin/buildin.go b/server/plugin/auth/buildin/buildin.go
index 9003c4e..a6640ee 100644
--- a/server/plugin/auth/buildin/buildin.go
+++ b/server/plugin/auth/buildin/buildin.go
@@ -26,7 +26,6 @@ import (
 	"github.com/apache/servicecomb-service-center/pkg/rest"
 	mgr "github.com/apache/servicecomb-service-center/server/plugin"
 	"github.com/apache/servicecomb-service-center/server/service/rbac"
-	"github.com/apache/servicecomb-service-center/server/service/rbac/dao"
 	"github.com/go-chassis/go-chassis/security/authr"
 	"github.com/go-chassis/go-chassis/server/restful"
 	"net/http"
@@ -67,7 +66,6 @@ func (ba *TokenAuthenticator) Identify(req *http.Request) error {
 		log.Errorf(err, "authenticate request failed, %s %s", req.Method, req.RequestURI)
 		return err
 	}
-	//TODO rbac
 	m, ok := claims.(map[string]interface{})
 	if !ok {
 		log.Error("claims convert failed", rbacframe.ErrConvertErr)
@@ -79,17 +77,38 @@ func (ba *TokenAuthenticator) Identify(req *http.Request) error {
 		log.Error("role convert failed", rbacframe.ErrConvertErr)
 		return rbacframe.ErrConvertErr
 	}
-	r := dao.GetResource(context.TODO(), req.URL.Path)
-	//TODO add verbs
-	allow, err := rbac.Allow(context.TODO(), roleName, req.URL.Query().Get(":project"), r, "")
+	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)
+	}
+	err = checkPerm(roleName, apiPattern)
+	if err != nil {
+		return err
+	}
+	req2 := req.WithContext(rbacframe.NewContext(req.Context(), claims))
+	*req = *req2
+	return nil
+}
+
+//this method decouple business code and perm checks
+func checkPerm(roleName, apiPattern string) error {
+	resource := rbacframe.GetResource(apiPattern)
+	if resource == "" {
+		//fast fail, no need to access role storage
+		return errors.New(errorsEx.MsgNoPerm)
+	}
+	//TODO add verbs,project
+	allow, err := rbac.Allow(context.TODO(), roleName, "", resource, "")
 	if err != nil {
 		log.Error("", err)
-		return errors.New(errorsEx.ErrMsgRolePerm)
+		return errors.New(errorsEx.MsgRolePerm)
 	}
 	if !allow {
-		return errors.New(errorsEx.ErrMsgNoPerm)
+		return errors.New(errorsEx.MsgNoPerm)
 	}
-	req2 := req.WithContext(rbacframe.NewContext(req.Context(), claims))
-	*req = *req2
 	return nil
 }
diff --git a/server/rest/controller/v4/auth_resource.go b/server/rest/controller/v4/auth_resource.go
index 70ab5b4..bbfa154 100644
--- a/server/rest/controller/v4/auth_resource.go
+++ b/server/rest/controller/v4/auth_resource.go
@@ -43,6 +43,9 @@ func (r *AuthResource) URLPatterns() []rest.Route {
 	return []rest.Route{
 		{Method: http.MethodPost, Path: "/v4/token", Func: r.Login},
 		{Method: http.MethodPost, Path: "/v4/account", Func: r.CreateAccount},
+		{Method: http.MethodGet, Path: "/v4/account", Func: r.ListAccount},
+		{Method: http.MethodGet, Path: "/v4/account/:name", Func: r.GetAccount},
+		{Method: http.MethodDelete, Path: "/v4/account/:name", Func: r.DeleteAccount},
 		{Method: http.MethodPost, Path: "/v4/account/:name/password", Func: r.ChangePassword},
 	}
 }
@@ -56,7 +59,7 @@ func (r *AuthResource) CreateAccount(w http.ResponseWriter, req *http.Request) {
 	a := &rbacframe.Account{}
 	if err = json.Unmarshal(body, a); err != nil {
 		log.Error("json err", err)
-		controller.WriteError(w, scerror.ErrInvalidParams, errorsEx.ErrMsgJSON)
+		controller.WriteError(w, scerror.ErrInvalidParams, errorsEx.MsgJSON)
 		return
 	}
 	err = service.ValidateCreateAccount(a)
@@ -70,11 +73,55 @@ func (r *AuthResource) CreateAccount(w http.ResponseWriter, req *http.Request) {
 			controller.WriteError(w, scerror.ErrConflictAccount, "")
 			return
 		}
-		log.Error(errorsEx.ErrMsgCreateAccount, err)
-		controller.WriteError(w, scerror.ErrInternal, errorsEx.ErrMsgCreateAccount)
+		log.Error(errorsEx.MsgOperateAccountFailed, err)
+		controller.WriteError(w, scerror.ErrInternal, errorsEx.MsgOperateAccountFailed)
 		return
 	}
 }
+func (r *AuthResource) DeleteAccount(w http.ResponseWriter, req *http.Request) {
+	_, err := dao.DeleteAccount(context.TODO(), req.URL.Query().Get(":name"))
+	if err != nil {
+		log.Error(errorsEx.MsgOperateAccountFailed, err)
+		controller.WriteError(w, scerror.ErrInternal, errorsEx.MsgOperateAccountFailed)
+		return
+	}
+	w.WriteHeader(http.StatusNoContent)
+}
+func (r *AuthResource) ListAccount(w http.ResponseWriter, req *http.Request) {
+	as, n, err := dao.ListAccount(context.TODO())
+	if err != nil {
+		log.Error(errorsEx.MsgGetAccountFailed, err)
+		controller.WriteError(w, scerror.ErrInternal, errorsEx.MsgGetAccountFailed)
+		return
+	}
+	resp := &rbacframe.AccountResponse{
+		Total:    n,
+		Accounts: as,
+	}
+	b, err := json.Marshal(resp)
+	if err != nil {
+		log.Error(errorsEx.MsgJSON, err)
+		controller.WriteError(w, scerror.ErrInternal, errorsEx.MsgJSON)
+		return
+	}
+	controller.WriteJSON(w, b)
+}
+func (r *AuthResource) GetAccount(w http.ResponseWriter, req *http.Request) {
+	a, err := dao.GetAccount(context.TODO(), req.URL.Query().Get(":name"))
+	if err != nil {
+		log.Error(errorsEx.MsgGetAccountFailed, err)
+		controller.WriteError(w, scerror.ErrInternal, errorsEx.MsgGetAccountFailed)
+		return
+	}
+	a.Password = ""
+	b, err := json.Marshal(a)
+	if err != nil {
+		log.Error(errorsEx.MsgJSON, err)
+		controller.WriteError(w, scerror.ErrInternal, errorsEx.MsgJSON)
+		return
+	}
+	controller.WriteJSON(w, b)
+}
 func (r *AuthResource) ChangePassword(w http.ResponseWriter, req *http.Request) {
 	ip := util.GetRealIP(req)
 	if rbac.IsBanned(ip) {
@@ -91,7 +138,7 @@ func (r *AuthResource) ChangePassword(w http.ResponseWriter, req *http.Request)
 	a := &rbacframe.Account{}
 	if err = json.Unmarshal(body, a); err != nil {
 		log.Error("json err", err)
-		controller.WriteError(w, scerror.ErrInvalidParams, errorsEx.ErrMsgJSON)
+		controller.WriteError(w, scerror.ErrInvalidParams, errorsEx.MsgJSON)
 		return
 	}
 	a.Name = req.URL.Query().Get(":name")
diff --git a/server/rest/controller/v4/auth_resource_test.go b/server/rest/controller/v4/auth_resource_test.go
index 273ad35..a84b67f 100644
--- a/server/rest/controller/v4/auth_resource_test.go
+++ b/server/rest/controller/v4/auth_resource_test.go
@@ -105,6 +105,92 @@ func TestAuthResource_Login(t *testing.T) {
 		rest.GetRouter().ServeHTTP(w, r)
 		assert.Equal(t, http.StatusOK, w.Code)
 	})
+
+}
+func TestAuthResource_DeleteAccount(t *testing.T) {
+	t.Run("dev_account can not even delete him self", func(t *testing.T) {
+		b, _ := json.Marshal(&rbacframe.Account{Name: "dev_account", Password: "Complicated_password2"})
+
+		r, _ := http.NewRequest(http.MethodPost, "/v4/token", bytes.NewBuffer(b))
+		w := httptest.NewRecorder()
+		rest.GetRouter().ServeHTTP(w, r)
+		assert.Equal(t, http.StatusOK, w.Code)
+		to := &rbacframe.Token{}
+		json.Unmarshal(w.Body.Bytes(), to)
+
+		r2, _ := http.NewRequest(http.MethodDelete, "/v4/account/dev_account", nil)
+		r2.Header.Set(restful.HeaderAuth, "Bearer "+to.TokenStr)
+		w2 := httptest.NewRecorder()
+		rest.GetRouter().ServeHTTP(w2, r2)
+		assert.Equal(t, http.StatusUnauthorized, w2.Code)
+	})
+	t.Run("root can delete account", func(t *testing.T) {
+		b, _ := json.Marshal(&rbacframe.Account{Name: "root", Password: "Complicated_password1"})
+		r, _ := http.NewRequest(http.MethodPost, "/v4/token", bytes.NewBuffer(b))
+		w := httptest.NewRecorder()
+		rest.GetRouter().ServeHTTP(w, r)
+		assert.Equal(t, http.StatusOK, w.Code)
+		to := &rbacframe.Token{}
+		json.Unmarshal(w.Body.Bytes(), to)
+
+		b, _ = json.Marshal(&rbacframe.Account{Name: "delete_account", Password: "Complicated_password1"})
+		r2, _ := http.NewRequest(http.MethodPost, "/v4/account", bytes.NewBuffer(b))
+		r2.Header.Set(restful.HeaderAuth, "Bearer "+to.TokenStr)
+		w2 := httptest.NewRecorder()
+		rest.GetRouter().ServeHTTP(w2, r2)
+		assert.Equal(t, http.StatusOK, w2.Code)
+
+		r3, _ := http.NewRequest(http.MethodDelete, "/v4/account/delete_account", nil)
+		r3.Header.Set(restful.HeaderAuth, "Bearer "+to.TokenStr)
+		w3 := httptest.NewRecorder()
+		rest.GetRouter().ServeHTTP(w3, r3)
+		assert.Equal(t, http.StatusNoContent, w3.Code)
+	})
+}
+func TestAuthResource_GetAccount(t *testing.T) {
+	t.Run("get account", func(t *testing.T) {
+		b, _ := json.Marshal(&rbacframe.Account{Name: "root", Password: "Complicated_password1"})
+		r, _ := http.NewRequest(http.MethodPost, "/v4/token", bytes.NewBuffer(b))
+		w := httptest.NewRecorder()
+		rest.GetRouter().ServeHTTP(w, r)
+		assert.Equal(t, http.StatusOK, w.Code)
+		to := &rbacframe.Token{}
+		json.Unmarshal(w.Body.Bytes(), to)
+
+		r3, _ := http.NewRequest(http.MethodGet, "/v4/account/dev_account", nil)
+		r3.Header.Set(restful.HeaderAuth, "Bearer "+to.TokenStr)
+		w3 := httptest.NewRecorder()
+		rest.GetRouter().ServeHTTP(w3, r3)
+		assert.Equal(t, http.StatusOK, w3.Code)
+
+		a := &rbacframe.Account{}
+		json.Unmarshal(w3.Body.Bytes(), a)
+		assert.Equal(t, "dev_account", a.Name)
+		assert.Equal(t, "developer", a.Role)
+		assert.Empty(t, a.Password)
+	})
+	t.Run("list account", func(t *testing.T) {
+		b, _ := json.Marshal(&rbacframe.Account{Name: "root", Password: "Complicated_password1"})
+		r, _ := http.NewRequest(http.MethodPost, "/v4/token", bytes.NewBuffer(b))
+		w := httptest.NewRecorder()
+		rest.GetRouter().ServeHTTP(w, r)
+		assert.Equal(t, http.StatusOK, w.Code)
+		to := &rbacframe.Token{}
+		json.Unmarshal(w.Body.Bytes(), to)
+
+		r3, _ := http.NewRequest(http.MethodGet, "/v4/account", nil)
+		r3.Header.Set(restful.HeaderAuth, "Bearer "+to.TokenStr)
+		w3 := httptest.NewRecorder()
+		rest.GetRouter().ServeHTTP(w3, r3)
+		assert.Equal(t, http.StatusOK, w3.Code)
+
+		a := &rbacframe.AccountResponse{}
+		json.Unmarshal(w3.Body.Bytes(), a)
+		assert.Greater(t, a.Total, int64(2))
+		assert.Empty(t, a.Accounts[0].Password)
+	})
+}
+func TestAuthResource_Login2(t *testing.T) {
 	t.Run("bock user dev_account", func(t *testing.T) {
 		b, _ := json.Marshal(&rbacframe.Account{Name: "dev_account", Password: "Complicated_password1"})
 
diff --git a/server/service/kv/store.go b/server/service/kv/store.go
index b9418bb..f47a42c 100644
--- a/server/service/kv/store.go
+++ b/server/service/kv/store.go
@@ -58,6 +58,16 @@ func Get(ctx context.Context, key string) (*mvccpb.KeyValue, error) {
 	return resp.Kvs[0], err
 }
 
+//Get get kv list
+func List(ctx context.Context, key string) ([]*mvccpb.KeyValue, int64, error) {
+	resp, err := backend.Registry().Do(ctx, registry.GET,
+		registry.WithStrKey(key), registry.WithPrefix())
+	if err != nil {
+		return nil, 0, err
+	}
+	return resp.Kvs, resp.Count, nil
+}
+
 //Exist get one kv, if can not get return false
 func Exist(ctx context.Context, key string) (bool, error) {
 	resp, err := backend.Registry().Do(ctx, registry.GET,
diff --git a/server/service/kv/store_test.go b/server/service/kv/store_test.go
index 2692460..3b27937 100644
--- a/server/service/kv/store_test.go
+++ b/server/service/kv/store_test.go
@@ -59,4 +59,15 @@ func TestStoreData(t *testing.T) {
 		assert.NoError(t, err)
 		assert.Equal(t, "value", string(r.Value))
 	})
+
+	t.Run("put many and list", func(t *testing.T) {
+		err := kv.Put(context.Background(), "/test/1", "value1")
+		assert.NoError(t, err)
+		err = kv.Put(context.Background(), "/test/2", "value2")
+		assert.NoError(t, err)
+		kvs, n, err := kv.List(context.Background(), "/test")
+		assert.NoError(t, err)
+		assert.Equal(t, int64(2), n)
+		t.Log(kvs)
+	})
 }
diff --git a/server/service/rbac/dao/account_dao.go b/server/service/rbac/dao/account_dao.go
index cf47b76..94500f8 100644
--- a/server/service/rbac/dao/account_dao.go
+++ b/server/service/rbac/dao/account_dao.go
@@ -26,6 +26,7 @@ import (
 	"github.com/apache/servicecomb-service-center/pkg/etcdsync"
 	"github.com/apache/servicecomb-service-center/pkg/log"
 	"github.com/apache/servicecomb-service-center/pkg/rbacframe"
+	"github.com/apache/servicecomb-service-center/pkg/util"
 	"github.com/apache/servicecomb-service-center/server/core"
 	"github.com/apache/servicecomb-service-center/server/service/kv"
 	stringutil "github.com/go-chassis/foundation/string"
@@ -63,6 +64,7 @@ func CreateAccount(ctx context.Context, a *rbacframe.Account) error {
 		return err
 	}
 	a.Password = stringutil.Bytes2str(hash)
+	a.ID = util.GenerateUUID()
 	value, err := json.Marshal(a)
 	if err != nil {
 		log.Errorf(err, "account info is invalid")
@@ -73,7 +75,7 @@ func CreateAccount(ctx context.Context, a *rbacframe.Account) error {
 		log.Errorf(err, "can not save account info")
 		return err
 	}
-
+	log.Info("create new account: " + a.ID)
 	return nil
 }
 
@@ -87,11 +89,31 @@ func GetAccount(ctx context.Context, name string) (*rbacframe.Account, error) {
 	a := &rbacframe.Account{}
 	err = json.Unmarshal(r.Value, a)
 	if err != nil {
-		log.Errorf(err, "account info is invalid format")
+		log.Errorf(err, "account info format invalid")
 		return nil, err
 	}
 	return a, nil
 }
+func ListAccount(ctx context.Context) ([]*rbacframe.Account, int64, error) {
+	key := core.GenerateAccountKey("")
+	r, n, err := kv.List(ctx, key)
+	if err != nil {
+		log.Errorf(err, "can not get account info")
+		return nil, 0, err
+	}
+	as := make([]*rbacframe.Account, 0, n)
+	for _, v := range r {
+		a := &rbacframe.Account{}
+		err = json.Unmarshal(v.Value, a)
+		if err != nil {
+			log.Error("account info format invalid:", err)
+			continue //do not fail if some account is invalid
+		}
+		a.Password = ""
+		as = append(as, a)
+	}
+	return as, n, nil
+}
 func AccountExist(ctx context.Context, name string) (bool, error) {
 	exist, err := kv.Exist(ctx, core.GenerateAccountKey(name))
 	if err != nil {
@@ -106,6 +128,7 @@ func DeleteAccount(ctx context.Context, name string) (bool, error) {
 		log.Errorf(err, "can not get account info")
 		return false, err
 	}
+	log.Info("account is deleted")
 	return exist, nil
 }
 
@@ -132,6 +155,6 @@ func EditAccount(ctx context.Context, a *rbacframe.Account) error {
 		log.Errorf(err, "can not edit account info")
 		return err
 	}
-
+	log.Info("account is edit")
 	return nil
 }
diff --git a/server/service/rbac/rbac.go b/server/service/rbac/rbac.go
index 54c0c70..e741b7c 100644
--- a/server/service/rbac/rbac.go
+++ b/server/service/rbac/rbac.go
@@ -38,6 +38,9 @@ const (
 	InitPassword = "SC_INIT_ROOT_PASSWORD"
 	PubFilePath  = "rbac_rsa_public_key_file"
 )
+const (
+	ResourceAccount = "account"
+)
 
 var (
 	ErrEmptyCurrentPassword = errors.New("current password should not be empty")
@@ -53,6 +56,7 @@ func Init() {
 		log.Info("rbac is disabled")
 		return
 	}
+	initResourceMap()
 	err := authr.Init()
 	if err != nil {
 		log.Fatal("can not enable auth module", err)
@@ -69,6 +73,13 @@ func Init() {
 	rbacframe.Add2WhiteAPIList("/v4/token")
 	log.Info("rbac is enabled")
 }
+func initResourceMap() {
+	rbacframe.MapResource("/v4/account", ResourceAccount)
+	rbacframe.MapResource("/v4/account/:name", ResourceAccount)
+	rbacframe.MapResource("/v4/role", "role")
+	//TODO now simply write dead code "*" to map all other API except account and role to service, should define resource for every API in future
+	rbacframe.MapResource("*", "service")
+}
 
 //readPublicKey read key to memory
 func readPrivateKey() {
diff --git a/server/service/rbac/rbca_test.go b/server/service/rbac/rbca_test.go
index c72b884..b2a819f 100644
--- a/server/service/rbac/rbca_test.go
+++ b/server/service/rbac/rbca_test.go
@@ -98,5 +98,10 @@ func TestInitRBAC(t *testing.T) {
 		assert.True(t, rbac.SamePassword(a.Password, "Complicated_password2"))
 
 	})
+	t.Run("list kv", func(t *testing.T) {
+		_, n, err := dao.ListAccount(context.TODO())
+		assert.NoError(t, err)
+		assert.Greater(t, n, int64(2))
+	})
 
 }