From 80204ba8e3c79fb9a146c0243e97bf946edda99b Mon Sep 17 00:00:00 2001 From: little-cui Date: Tue, 28 Dec 2021 20:39:41 +0800 Subject: [PATCH] Optimize: refactor rbac datasource interface --- datasource/account_test.go | 121 --------------------- datasource/datasource.go | 3 - datasource/etcd/account.go | 46 +++++--- datasource/etcd/account_lock.go | 31 ++---- datasource/etcd/etcd.go | 28 +---- datasource/etcd/role.go | 31 +++--- datasource/manager.go | 19 +--- datasource/mongo/account.go | 40 ++++--- datasource/mongo/account_lock.go | 33 +++--- datasource/mongo/mongo.go | 28 +---- datasource/mongo/role.go | 36 +++--- datasource/{ => rbac}/account.go | 20 ++-- datasource/rbac/account_test.go | 118 ++++++++++++++++++++ datasource/rbac/init.go | 62 +++++++++++ datasource/rbac/options.go | 23 ++++ datasource/rbac/rbac.go | 24 ++++ datasource/{ => rbac}/role.go | 2 +- datasource/{ => rbac}/role_test.go | 46 ++++---- datasource/schema/init.go | 8 +- server/job/account/account_test.go | 18 +-- server/plugin/quota/buildin/account.go | 4 +- server/plugin/quota/buildin/role.go | 4 +- server/resource/rbac/auth_resource.go | 24 ++-- server/resource/rbac/auth_resource_test.go | 4 +- server/service/account/account.go | 28 ++--- server/service/account/account_test.go | 42 +++---- server/service/rbac/account_service.go | 56 +++++----- server/service/rbac/blocker.go | 6 +- server/service/rbac/blocker_test.go | 10 +- server/service/rbac/decision.go | 24 ++-- server/service/rbac/perm_service.go | 14 +-- server/service/rbac/role_service.go | 51 +++++---- 32 files changed, 529 insertions(+), 475 deletions(-) delete mode 100644 datasource/account_test.go rename datasource/{ => rbac}/account.go (84%) create mode 100644 datasource/rbac/account_test.go create mode 100644 datasource/rbac/init.go create mode 100644 datasource/rbac/options.go create mode 100644 datasource/rbac/rbac.go rename datasource/{ => rbac}/role.go (98%) rename datasource/{ => rbac}/role_test.go (63%) diff --git a/datasource/account_test.go b/datasource/account_test.go deleted file mode 100644 index a6e839b84..000000000 --- a/datasource/account_test.go +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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 datasource_test - -import ( - "context" - "strconv" - "testing" - "time" - - "github.com/go-chassis/cari/rbac" - - "github.com/apache/servicecomb-service-center/datasource" - "github.com/stretchr/testify/assert" -) - -var ( - a1 = rbac.Account{ - ID: "11111-22222-33333", - Name: "test-account1", - Password: "tnuocca-tset", - Roles: []string{"admin"}, - TokenExpirationTime: "2020-12-30", - CurrentPassword: "tnuocca-tset1", - } - a2 = rbac.Account{ - ID: "11111-22222-33333-44444", - Name: "test-account2", - Password: "tnuocca-tset", - Roles: []string{"admin"}, - TokenExpirationTime: "2020-12-30", - CurrentPassword: "tnuocca-tset2", - } -) - -func TestAccount(t *testing.T) { - t.Run("add and get account", func(t *testing.T) { - err := datasource.GetAccountManager().CreateAccount(context.Background(), &a1) - assert.NoError(t, err) - err = datasource.GetAccountManager().CreateAccount(context.Background(), &a2) - assert.NoError(t, err) - r, err := datasource.GetAccountManager().GetAccount(context.Background(), a1.Name) - assert.NoError(t, err) - assert.Equal(t, a1, *r) - _, err = datasource.GetAccountManager().DeleteAccount(context.Background(), []string{a1.Name, a2.Name}) - assert.NoError(t, err) - }) - t.Run("account should exist", func(t *testing.T) { - err := datasource.GetAccountManager().CreateAccount(context.Background(), &a1) - assert.NoError(t, err) - exist, err := datasource.GetAccountManager().AccountExist(context.Background(), a1.Name) - assert.NoError(t, err) - assert.True(t, exist) - _, err = datasource.GetAccountManager().DeleteAccount(context.Background(), []string{a1.Name}) - assert.NoError(t, err) - }) - t.Run("delete account", func(t *testing.T) { - err := datasource.GetAccountManager().CreateAccount(context.Background(), &a2) - assert.NoError(t, err) - _, err = datasource.GetAccountManager().DeleteAccount(context.Background(), []string{a2.Name}) - assert.NoError(t, err) - }) - t.Run("add and update accounts then list", func(t *testing.T) { - err := datasource.GetAccountManager().CreateAccount(context.Background(), &a1) - assert.NoError(t, err) - err = datasource.GetAccountManager().CreateAccount(context.Background(), &a2) - assert.NoError(t, err) - a2.Password = "new-password" - err = datasource.GetAccountManager().UpdateAccount(context.Background(), a2.Name, &a2) - assert.NoError(t, err) - accounts, _, err := datasource.GetAccountManager().ListAccount(context.Background()) - assert.NoError(t, err) - assert.GreaterOrEqual(t, len(accounts), 2) - _, err = datasource.GetAccountManager().DeleteAccount(context.Background(), []string{a1.Name}) - assert.NoError(t, err) - _, err = datasource.GetAccountManager().DeleteAccount(context.Background(), []string{a2.Name}) - assert.NoError(t, err) - }) - t.Run("add and update accounts, should have create/update time", func(t *testing.T) { - err := datasource.GetAccountManager().CreateAccount(context.Background(), &a1) - assert.NoError(t, err) - - r, err := datasource.GetAccountManager().GetAccount(context.Background(), a1.Name) - assert.NoError(t, err) - dt, _ := strconv.Atoi(r.CreateTime) - assert.Less(t, 0, dt) - assert.Equal(t, r.CreateTime, r.UpdateTime) - - time.Sleep(time.Second) - a1.Password = "new-password" - err = datasource.GetAccountManager().UpdateAccount(context.Background(), a1.Name, &a1) - assert.NoError(t, err) - - old, _ := strconv.Atoi(r.UpdateTime) - r, err = datasource.GetAccountManager().GetAccount(context.Background(), a1.Name) - assert.NoError(t, err) - last, _ := strconv.Atoi(r.UpdateTime) - assert.Less(t, old, last) - assert.NotEqual(t, r.CreateTime, r.UpdateTime) - - _, err = datasource.GetAccountManager().DeleteAccount(context.Background(), []string{a1.Name}) - assert.NoError(t, err) - }) -} diff --git a/datasource/datasource.go b/datasource/datasource.go index cc1efce08..ae0727b08 100644 --- a/datasource/datasource.go +++ b/datasource/datasource.go @@ -20,9 +20,6 @@ package datasource // DataSource is the DAO layer type DataSource interface { SystemManager() SystemManager - AccountManager() AccountManager - AccountLockManager() AccountLockManager - RoleManager() RoleManager DependencyManager() DependencyManager MetadataManager() MetadataManager SCManager() SCManager diff --git a/datasource/etcd/account.go b/datasource/etcd/account.go index 8159c7c92..529e7e550 100644 --- a/datasource/etcd/account.go +++ b/datasource/etcd/account.go @@ -22,21 +22,31 @@ import ( "strconv" "time" - "github.com/apache/servicecomb-service-center/datasource" "github.com/apache/servicecomb-service-center/datasource/etcd/path" + "github.com/apache/servicecomb-service-center/datasource/rbac" "github.com/apache/servicecomb-service-center/pkg/etcdsync" "github.com/apache/servicecomb-service-center/pkg/log" "github.com/apache/servicecomb-service-center/pkg/privacy" "github.com/apache/servicecomb-service-center/pkg/util" - "github.com/go-chassis/cari/rbac" + rbacmodel "github.com/go-chassis/cari/rbac" "github.com/go-chassis/foundation/stringutil" "github.com/little-cui/etcdadpt" ) -type AccountManager struct { +func init() { + rbac.Install("etcd", NewRbacDAO) + rbac.Install("embeded_etcd", NewRbacDAO) + rbac.Install("embedded_etcd", NewRbacDAO) } -func (ds *AccountManager) CreateAccount(ctx context.Context, a *rbac.Account) error { +func NewRbacDAO(opts rbac.Options) (rbac.DAO, error) { + return &RbacDAO{}, nil +} + +type RbacDAO struct { +} + +func (ds *RbacDAO) CreateAccount(ctx context.Context, a *rbacmodel.Account) error { lock, err := etcdsync.Lock("/account-creating/"+a.Name, -1, false) if err != nil { return fmt.Errorf("account %s is creating", a.Name) @@ -53,7 +63,7 @@ func (ds *AccountManager) CreateAccount(ctx context.Context, a *rbac.Account) er return err } if exist { - return datasource.ErrAccountDuplicated + return rbac.ErrAccountDuplicated } a.Password, err = privacy.ScryptPassword(a.Password) if err != nil { @@ -78,7 +88,7 @@ func (ds *AccountManager) CreateAccount(ctx context.Context, a *rbac.Account) er log.Info("create new account: " + a.ID) return nil } -func GenAccountOpts(a *rbac.Account, action etcdadpt.Action) ([]etcdadpt.OpOptions, error) { +func GenAccountOpts(a *rbacmodel.Account, action etcdadpt.Action) ([]etcdadpt.OpOptions, error) { opts := make([]etcdadpt.OpOptions, 0) value, err := json.Marshal(a) if err != nil { @@ -100,18 +110,18 @@ func GenAccountOpts(a *rbac.Account, action etcdadpt.Action) ([]etcdadpt.OpOptio return opts, nil } -func (ds *AccountManager) AccountExist(ctx context.Context, name string) (bool, error) { +func (ds *RbacDAO) AccountExist(ctx context.Context, name string) (bool, error) { return etcdadpt.Exist(ctx, path.GenerateRBACAccountKey(name)) } -func (ds *AccountManager) GetAccount(ctx context.Context, name string) (*rbac.Account, error) { +func (ds *RbacDAO) GetAccount(ctx context.Context, name string) (*rbacmodel.Account, error) { kv, err := etcdadpt.Get(ctx, path.GenerateRBACAccountKey(name)) if err != nil { return nil, err } if kv == nil { - return nil, datasource.ErrAccountNotExist + return nil, rbac.ErrAccountNotExist } - account := &rbac.Account{} + account := &rbacmodel.Account{} err = json.Unmarshal(kv.Value, account) if err != nil { log.Error("account info format invalid", err) @@ -121,7 +131,7 @@ func (ds *AccountManager) GetAccount(ctx context.Context, name string) (*rbac.Ac return account, nil } -func (ds *AccountManager) compatibleOldVersionAccount(a *rbac.Account) { +func (ds *RbacDAO) compatibleOldVersionAccount(a *rbacmodel.Account) { // old version use Role, now use Roles // Role/Roles will not exist at the same time if len(a.Role) == 0 { @@ -131,14 +141,14 @@ func (ds *AccountManager) compatibleOldVersionAccount(a *rbac.Account) { a.Role = "" } -func (ds *AccountManager) ListAccount(ctx context.Context) ([]*rbac.Account, int64, error) { +func (ds *RbacDAO) ListAccount(ctx context.Context) ([]*rbacmodel.Account, int64, error) { kvs, n, err := etcdadpt.List(ctx, path.GenerateRBACAccountKey("")) if err != nil { return nil, 0, err } - accounts := make([]*rbac.Account, 0, n) + accounts := make([]*rbacmodel.Account, 0, n) for _, v := range kvs { - a := &rbac.Account{} + a := &rbacmodel.Account{} err = json.Unmarshal(v.Value, a) if err != nil { log.Error("account info format invalid:", err) @@ -150,7 +160,7 @@ func (ds *AccountManager) ListAccount(ctx context.Context) ([]*rbac.Account, int } return accounts, n, nil } -func (ds *AccountManager) DeleteAccount(ctx context.Context, names []string) (bool, error) { +func (ds *RbacDAO) DeleteAccount(ctx context.Context, names []string) (bool, error) { if len(names) == 0 { return false, nil } @@ -172,13 +182,13 @@ func (ds *AccountManager) DeleteAccount(ctx context.Context, names []string) (bo } err = etcdadpt.Txn(ctx, opts) if err != nil { - log.Error(datasource.ErrDeleteAccountFailed.Error(), err) + log.Error(rbac.ErrDeleteAccountFailed.Error(), err) return false, err } } return true, nil } -func (ds *AccountManager) UpdateAccount(ctx context.Context, name string, account *rbac.Account) error { +func (ds *RbacDAO) UpdateAccount(ctx context.Context, name string, account *rbacmodel.Account) error { var ( opts []etcdadpt.OpOptions err error @@ -216,7 +226,7 @@ func (ds *AccountManager) UpdateAccount(ctx context.Context, name string, accoun return err } -func hasRole(account *rbac.Account, r string) bool { +func hasRole(account *rbacmodel.Account, r string) bool { for _, n := range account.Roles { if r == n { return true diff --git a/datasource/etcd/account_lock.go b/datasource/etcd/account_lock.go index d1c9ee697..76ed58d16 100644 --- a/datasource/etcd/account_lock.go +++ b/datasource/etcd/account_lock.go @@ -20,16 +20,13 @@ import ( "encoding/json" "fmt" - "github.com/apache/servicecomb-service-center/datasource" "github.com/apache/servicecomb-service-center/datasource/etcd/path" + "github.com/apache/servicecomb-service-center/datasource/rbac" "github.com/apache/servicecomb-service-center/pkg/log" "github.com/little-cui/etcdadpt" ) -type AccountLockManager struct { -} - -func (al AccountLockManager) UpsertLock(ctx context.Context, lock *datasource.AccountLock) error { +func (al *RbacDAO) UpsertLock(ctx context.Context, lock *rbac.Lock) error { value, err := json.Marshal(lock) if err != nil { log.Error("account lock is invalid", err) @@ -46,15 +43,15 @@ func (al AccountLockManager) UpsertLock(ctx context.Context, lock *datasource.Ac return nil } -func (al AccountLockManager) GetLock(ctx context.Context, key string) (*datasource.AccountLock, error) { +func (al *RbacDAO) GetLock(ctx context.Context, key string) (*rbac.Lock, error) { kv, err := etcdadpt.Get(ctx, path.GenerateAccountLockKey(key)) if err != nil { return nil, err } if kv == nil { - return nil, datasource.ErrAccountLockNotExist + return nil, rbac.ErrAccountLockNotExist } - lock := &datasource.AccountLock{} + lock := &rbac.Lock{} err = json.Unmarshal(kv.Value, lock) if err != nil { log.Error(fmt.Sprintf("key %s format invalid", key), err) @@ -63,14 +60,14 @@ func (al AccountLockManager) GetLock(ctx context.Context, key string) (*datasour return lock, nil } -func (al AccountLockManager) ListLock(ctx context.Context) ([]*datasource.AccountLock, int64, error) { +func (al *RbacDAO) ListLock(ctx context.Context) ([]*rbac.Lock, int64, error) { kvs, n, err := etcdadpt.List(ctx, path.GenerateAccountLockKey("")) if err != nil { return nil, 0, err } - locks := make([]*datasource.AccountLock, 0, n) + locks := make([]*rbac.Lock, 0, n) for _, v := range kvs { - lock := &datasource.AccountLock{} + lock := &rbac.Lock{} err = json.Unmarshal(v.Value, lock) if err != nil { log.Error("account lock info format invalid:", err) @@ -81,17 +78,17 @@ func (al AccountLockManager) ListLock(ctx context.Context) ([]*datasource.Accoun return locks, n, nil } -func (al AccountLockManager) DeleteLock(ctx context.Context, key string) error { +func (al *RbacDAO) DeleteLock(ctx context.Context, key string) error { _, err := etcdadpt.Delete(ctx, path.GenerateAccountLockKey(key)) if err != nil { log.Error(fmt.Sprintf("remove lock %s failed", key), err) - return datasource.ErrCannotReleaseLock + return rbac.ErrCannotReleaseLock } log.Info(fmt.Sprintf("%s is released", key)) return nil } -func (al AccountLockManager) DeleteLockList(ctx context.Context, keys []string) error { +func (al *RbacDAO) DeleteLockList(ctx context.Context, keys []string) error { var opts []etcdadpt.OpOptions for _, key := range keys { opts = append(opts, etcdadpt.OpDel(etcdadpt.WithStrKey(path.GenerateAccountLockKey(key)))) @@ -102,12 +99,8 @@ func (al AccountLockManager) DeleteLockList(ctx context.Context, keys []string) err := etcdadpt.Txn(ctx, opts) if err != nil { log.Error(fmt.Sprintf("remove locks %v failed", keys), err) - return datasource.ErrCannotReleaseLock + return rbac.ErrCannotReleaseLock } log.Info(fmt.Sprintf("%v are released", keys)) return nil } - -func NewAccountLockManager() datasource.AccountLockManager { - return &AccountLockManager{} -} diff --git a/datasource/etcd/etcd.go b/datasource/etcd/etcd.go index 42e5e5ab7..75ebf7298 100644 --- a/datasource/etcd/etcd.go +++ b/datasource/etcd/etcd.go @@ -48,32 +48,17 @@ func init() { type DataSource struct { Options *datasource.Options - accountLockManager datasource.AccountLockManager - accountManager datasource.AccountManager - metadataManager datasource.MetadataManager - roleManager datasource.RoleManager - sysManager datasource.SystemManager - depManager datasource.DependencyManager - scManager datasource.SCManager - metricsManager datasource.MetricsManager -} - -func (ds *DataSource) AccountLockManager() datasource.AccountLockManager { - return ds.accountLockManager + metadataManager datasource.MetadataManager + sysManager datasource.SystemManager + depManager datasource.DependencyManager + scManager datasource.SCManager + metricsManager datasource.MetricsManager } func (ds *DataSource) SystemManager() datasource.SystemManager { return ds.sysManager } -func (ds *DataSource) AccountManager() datasource.AccountManager { - return ds.accountManager -} - -func (ds *DataSource) RoleManager() datasource.RoleManager { - return ds.roleManager -} - func (ds *DataSource) DependencyManager() datasource.DependencyManager { return ds.depManager } @@ -109,9 +94,6 @@ func NewDataSource(opts datasource.Options) (datasource.DataSource, error) { if err := inst.initialize(); err != nil { return nil, err } - inst.accountManager = &AccountManager{} - inst.accountLockManager = NewAccountLockManager() - inst.roleManager = &RoleManager{} inst.metadataManager = newMetadataManager(opts.SchemaNotEditable, opts.InstanceTTL) inst.sysManager = newSysManager() inst.depManager = &DepManager{} diff --git a/datasource/etcd/role.go b/datasource/etcd/role.go index 467d64015..f1a109279 100644 --- a/datasource/etcd/role.go +++ b/datasource/etcd/role.go @@ -24,19 +24,16 @@ import ( "strconv" "time" - "github.com/apache/servicecomb-service-center/datasource" "github.com/apache/servicecomb-service-center/datasource/etcd/path" + "github.com/apache/servicecomb-service-center/datasource/rbac" "github.com/apache/servicecomb-service-center/pkg/etcdsync" "github.com/apache/servicecomb-service-center/pkg/log" "github.com/apache/servicecomb-service-center/pkg/util" - "github.com/go-chassis/cari/rbac" + rbacmodel "github.com/go-chassis/cari/rbac" "github.com/little-cui/etcdadpt" ) -type RoleManager struct { -} - -func (rm *RoleManager) CreateRole(ctx context.Context, r *rbac.Role) error { +func (rm *RbacDAO) CreateRole(ctx context.Context, r *rbacmodel.Role) error { lock, err := etcdsync.Lock("/role-creating/"+r.Name, -1, false) if err != nil { return fmt.Errorf("role %s is creating", r.Name) @@ -54,7 +51,7 @@ func (rm *RoleManager) CreateRole(ctx context.Context, r *rbac.Role) error { return err } if exist { - return datasource.ErrRoleDuplicated + return rbac.ErrRoleDuplicated } r.ID = util.GenerateUUID() r.CreateTime = strconv.FormatInt(time.Now().Unix(), 10) @@ -73,19 +70,19 @@ func (rm *RoleManager) CreateRole(ctx context.Context, r *rbac.Role) error { return nil } -func (rm *RoleManager) RoleExist(ctx context.Context, name string) (bool, error) { +func (rm *RbacDAO) RoleExist(ctx context.Context, name string) (bool, error) { return etcdadpt.Exist(ctx, path.GenerateRBACRoleKey(name)) } -func (rm *RoleManager) GetRole(ctx context.Context, name string) (*rbac.Role, error) { +func (rm *RbacDAO) GetRole(ctx context.Context, name string) (*rbacmodel.Role, error) { kv, err := etcdadpt.Get(ctx, path.GenerateRBACRoleKey(name)) if err != nil { return nil, err } if kv == nil { - return nil, datasource.ErrRoleNotExist + return nil, rbac.ErrRoleNotExist } - role := &rbac.Role{} + role := &rbacmodel.Role{} err = json.Unmarshal(kv.Value, role) if err != nil { log.Error("role info format invalid", err) @@ -93,14 +90,14 @@ func (rm *RoleManager) GetRole(ctx context.Context, name string) (*rbac.Role, er } return role, nil } -func (rm *RoleManager) ListRole(ctx context.Context) ([]*rbac.Role, int64, error) { +func (rm *RbacDAO) ListRole(ctx context.Context) ([]*rbacmodel.Role, int64, error) { kvs, n, err := etcdadpt.List(ctx, path.GenerateRBACRoleKey("")) if err != nil { return nil, 0, err } - roles := make([]*rbac.Role, 0, n) + roles := make([]*rbacmodel.Role, 0, n) for _, v := range kvs { - r := &rbac.Role{} + r := &rbacmodel.Role{} err = json.Unmarshal(v.Value, r) if err != nil { log.Error("role info format invalid:", err) @@ -111,14 +108,14 @@ func (rm *RoleManager) ListRole(ctx context.Context) ([]*rbac.Role, int64, error } return roles, n, nil } -func (rm *RoleManager) DeleteRole(ctx context.Context, name string) (bool, error) { +func (rm *RbacDAO) DeleteRole(ctx context.Context, name string) (bool, error) { exists, err := RoleBindingExists(ctx, name) if err != nil { log.Error("", err) return false, err } if exists { - return false, datasource.ErrRoleBindingExist + return false, rbac.ErrRoleBindingExist } del, err := etcdadpt.Delete(ctx, path.GenerateRBACRoleKey(name)) if err != nil { @@ -134,7 +131,7 @@ func RoleBindingExists(ctx context.Context, role string) (bool, error) { } return total > 0, nil } -func (rm *RoleManager) UpdateRole(ctx context.Context, name string, role *rbac.Role) error { +func (rm *RbacDAO) UpdateRole(ctx context.Context, name string, role *rbacmodel.Role) error { role.UpdateTime = strconv.FormatInt(time.Now().Unix(), 10) value, err := json.Marshal(role) if err != nil { diff --git a/datasource/manager.go b/datasource/manager.go index 9ac3e5dd4..5cb56dc65 100644 --- a/datasource/manager.go +++ b/datasource/manager.go @@ -20,6 +20,7 @@ package datasource import ( "fmt" + "github.com/apache/servicecomb-service-center/datasource/rbac" "github.com/apache/servicecomb-service-center/datasource/schema" "github.com/apache/servicecomb-service-center/pkg/log" ) @@ -47,10 +48,11 @@ func Init(opts Options) error { if err != nil { return err } - - err = schema.Init(schema.Options{ - Kind: opts.Kind, - }) + err = schema.Init(schema.Options{Kind: opts.Kind}) + if err != nil { + return err + } + err = rbac.Init(rbac.Options{Kind: opts.Kind}) if err != nil { return err } @@ -80,15 +82,6 @@ func GetMetadataManager() MetadataManager { func GetSystemManager() SystemManager { return dataSourceInst.SystemManager() } -func GetRoleManager() RoleManager { - return dataSourceInst.RoleManager() -} -func GetAccountManager() AccountManager { - return dataSourceInst.AccountManager() -} -func GetAccountLockManager() AccountLockManager { - return dataSourceInst.AccountLockManager() -} func GetDependencyManager() DependencyManager { return dataSourceInst.DependencyManager() } diff --git a/datasource/mongo/account.go b/datasource/mongo/account.go index ecc2c3deb..06dd65e9a 100644 --- a/datasource/mongo/account.go +++ b/datasource/mongo/account.go @@ -23,10 +23,10 @@ import ( "strconv" "time" - "github.com/go-chassis/cari/rbac" + "github.com/apache/servicecomb-service-center/datasource/rbac" + rbacmodel "github.com/go-chassis/cari/rbac" "go.mongodb.org/mongo-driver/mongo" - "github.com/apache/servicecomb-service-center/datasource" "github.com/apache/servicecomb-service-center/datasource/mongo/client" "github.com/apache/servicecomb-service-center/datasource/mongo/client/model" mutil "github.com/apache/servicecomb-service-center/datasource/mongo/util" @@ -35,10 +35,18 @@ import ( "github.com/apache/servicecomb-service-center/pkg/util" ) -type AccountManager struct { +func init() { + rbac.Install("mongo", NewRbacDAO) } -func (ds *AccountManager) CreateAccount(ctx context.Context, a *rbac.Account) error { +func NewRbacDAO(opts rbac.Options) (rbac.DAO, error) { + return &RbacDAO{}, nil +} + +type RbacDAO struct { +} + +func (ds *RbacDAO) CreateAccount(ctx context.Context, a *rbacmodel.Account) error { exist, err := ds.AccountExist(ctx, a.Name) if err != nil { msg := fmt.Sprintf("failed to query account, account name %s", a.Name) @@ -46,7 +54,7 @@ func (ds *AccountManager) CreateAccount(ctx context.Context, a *rbac.Account) er return err } if exist { - return datasource.ErrAccountDuplicated + return rbac.ErrAccountDuplicated } a.Password, err = privacy.ScryptPassword(a.Password) if err != nil { @@ -62,7 +70,7 @@ func (ds *AccountManager) CreateAccount(ctx context.Context, a *rbac.Account) er _, err = client.GetMongoClient().Insert(ctx, model.CollectionAccount, a) if err != nil { if client.IsDuplicateKey(err) { - return datasource.ErrAccountDuplicated + return rbac.ErrAccountDuplicated } return err } @@ -70,7 +78,7 @@ func (ds *AccountManager) CreateAccount(ctx context.Context, a *rbac.Account) er return nil } -func (ds *AccountManager) AccountExist(ctx context.Context, name string) (bool, error) { +func (ds *RbacDAO) AccountExist(ctx context.Context, name string) (bool, error) { filter := mutil.NewFilter(mutil.AccountName(name)) count, err := client.GetMongoClient().Count(ctx, model.CollectionAccount, filter) if err != nil { @@ -82,7 +90,7 @@ func (ds *AccountManager) AccountExist(ctx context.Context, name string) (bool, return true, nil } -func (ds *AccountManager) GetAccount(ctx context.Context, name string) (*rbac.Account, error) { +func (ds *RbacDAO) GetAccount(ctx context.Context, name string) (*rbacmodel.Account, error) { filter := mutil.NewFilter(mutil.AccountName(name)) result, err := client.GetMongoClient().FindOne(ctx, model.CollectionAccount, filter) if err != nil { @@ -92,13 +100,13 @@ func (ds *AccountManager) GetAccount(ctx context.Context, name string) (*rbac.Ac } if err = result.Err(); err != nil { if err == mongo.ErrNoDocuments { - return nil, datasource.ErrAccountNotExist + return nil, rbac.ErrAccountNotExist } msg := fmt.Sprintf("failed to query account, account name %s", name) log.Error(msg, result.Err()) - return nil, datasource.ErrQueryAccountFailed + return nil, rbac.ErrQueryAccountFailed } - var account rbac.Account + var account rbacmodel.Account err = result.Decode(&account) if err != nil { log.Error("failed to decode account", err) @@ -107,16 +115,16 @@ func (ds *AccountManager) GetAccount(ctx context.Context, name string) (*rbac.Ac return &account, nil } -func (ds *AccountManager) ListAccount(ctx context.Context) ([]*rbac.Account, int64, error) { +func (ds *RbacDAO) ListAccount(ctx context.Context) ([]*rbacmodel.Account, int64, error) { filter := mutil.NewFilter() cursor, err := client.GetMongoClient().Find(ctx, model.CollectionAccount, filter) if err != nil { return nil, 0, err } - var accounts []*rbac.Account + var accounts []*rbacmodel.Account defer cursor.Close(ctx) for cursor.Next(ctx) { - var account rbac.Account + var account rbacmodel.Account err = cursor.Decode(&account) if err != nil { log.Error("failed to decode account", err) @@ -128,7 +136,7 @@ func (ds *AccountManager) ListAccount(ctx context.Context) ([]*rbac.Account, int return accounts, int64(len(accounts)), nil } -func (ds *AccountManager) DeleteAccount(ctx context.Context, names []string) (bool, error) { +func (ds *RbacDAO) DeleteAccount(ctx context.Context, names []string) (bool, error) { if len(names) == 0 { return false, nil } @@ -144,7 +152,7 @@ func (ds *AccountManager) DeleteAccount(ctx context.Context, names []string) (bo return true, nil } -func (ds *AccountManager) UpdateAccount(ctx context.Context, name string, account *rbac.Account) error { +func (ds *RbacDAO) UpdateAccount(ctx context.Context, name string, account *rbacmodel.Account) error { filter := mutil.NewFilter(mutil.AccountName(name)) setFilter := mutil.NewFilter( mutil.ID(account.ID), diff --git a/datasource/mongo/account_lock.go b/datasource/mongo/account_lock.go index e3c00228f..484e89efc 100644 --- a/datasource/mongo/account_lock.go +++ b/datasource/mongo/account_lock.go @@ -19,19 +19,16 @@ import ( "context" "fmt" - "github.com/apache/servicecomb-service-center/datasource" "github.com/apache/servicecomb-service-center/datasource/mongo/client" "github.com/apache/servicecomb-service-center/datasource/mongo/client/model" mutil "github.com/apache/servicecomb-service-center/datasource/mongo/util" + "github.com/apache/servicecomb-service-center/datasource/rbac" "github.com/apache/servicecomb-service-center/pkg/log" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) -type AccountLockManager struct { -} - -func (al *AccountLockManager) UpsertLock(ctx context.Context, lock *datasource.AccountLock) error { +func (al *RbacDAO) UpsertLock(ctx context.Context, lock *rbac.Lock) error { key := lock.Key releaseAt := lock.ReleaseAt filter := mutil.NewFilter(mutil.AccountLockKey(key)) @@ -54,7 +51,7 @@ func (al *AccountLockManager) UpsertLock(ctx context.Context, lock *datasource.A return nil } -func (al *AccountLockManager) GetLock(ctx context.Context, key string) (*datasource.AccountLock, error) { +func (al *RbacDAO) GetLock(ctx context.Context, key string) (*rbac.Lock, error) { filter := mutil.NewFilter(mutil.AccountLockKey(key)) result, err := client.GetMongoClient().FindOne(ctx, model.CollectionAccountLock, filter) if err != nil { @@ -62,13 +59,13 @@ func (al *AccountLockManager) GetLock(ctx context.Context, key string) (*datasou } if err = result.Err(); err != nil { if err == mongo.ErrNoDocuments { - return nil, datasource.ErrAccountLockNotExist + return nil, rbac.ErrAccountLockNotExist } msg := fmt.Sprintf("failed to query account lock, key %s", key) log.Error(msg, result.Err()) - return nil, datasource.ErrQueryAccountLockFailed + return nil, rbac.ErrQueryAccountLockFailed } - var lock datasource.AccountLock + var lock rbac.Lock err = result.Decode(&lock) if err != nil { log.Error(fmt.Sprintf("failed to decode account lock %s", key), err) @@ -77,16 +74,16 @@ func (al *AccountLockManager) GetLock(ctx context.Context, key string) (*datasou return &lock, nil } -func (al *AccountLockManager) ListLock(ctx context.Context) ([]*datasource.AccountLock, int64, error) { +func (al *RbacDAO) ListLock(ctx context.Context) ([]*rbac.Lock, int64, error) { filter := mutil.NewFilter() cursor, err := client.GetMongoClient().Find(ctx, model.CollectionAccountLock, filter) if err != nil { return nil, 0, err } - var locks []*datasource.AccountLock + var locks []*rbac.Lock defer cursor.Close(ctx) for cursor.Next(ctx) { - var lock datasource.AccountLock + var lock rbac.Lock err = cursor.Decode(&lock) if err != nil { log.Error("failed to decode account lock", err) @@ -97,18 +94,18 @@ func (al *AccountLockManager) ListLock(ctx context.Context) ([]*datasource.Accou return locks, int64(len(locks)), nil } -func (al *AccountLockManager) DeleteLock(ctx context.Context, key string) error { +func (al *RbacDAO) DeleteLock(ctx context.Context, key string) error { filter := mutil.NewFilter(mutil.AccountLockKey(key)) _, err := client.GetMongoClient().Delete(ctx, model.CollectionAccountLock, filter) if err != nil { log.Error(fmt.Sprintf("remove lock %s failed", key), err) - return datasource.ErrCannotReleaseLock + return rbac.ErrCannotReleaseLock } log.Info(fmt.Sprintf("%s is released", key)) return nil } -func (al *AccountLockManager) DeleteLockList(ctx context.Context, keys []string) error { +func (al *RbacDAO) DeleteLockList(ctx context.Context, keys []string) error { var delKeys []mongo.WriteModel for _, key := range keys { delKeys = append(delKeys, mongo.NewDeleteOneModel().SetFilter(mutil.NewFilter(mutil.AccountLockKey(key)))) @@ -119,12 +116,8 @@ func (al *AccountLockManager) DeleteLockList(ctx context.Context, keys []string) _, err := client.GetMongoClient().BatchDelete(ctx, model.CollectionAccountLock, delKeys) if err != nil { log.Error(fmt.Sprintf("remove locks %v failed", keys), err) - return datasource.ErrCannotReleaseLock + return rbac.ErrCannotReleaseLock } log.Info(fmt.Sprintf("%v are released", keys)) return nil } - -func NewAccountLockManager() datasource.AccountLockManager { - return &AccountLockManager{} -} diff --git a/datasource/mongo/mongo.go b/datasource/mongo/mongo.go index 146010280..8bac3c41d 100644 --- a/datasource/mongo/mongo.go +++ b/datasource/mongo/mongo.go @@ -37,32 +37,17 @@ func init() { } type DataSource struct { - accountLockManager datasource.AccountLockManager - accountManager datasource.AccountManager - metadataManager datasource.MetadataManager - roleManager datasource.RoleManager - sysManager datasource.SystemManager - depManager datasource.DependencyManager - scManager datasource.SCManager - metricsManager datasource.MetricsManager -} - -func (ds *DataSource) AccountLockManager() datasource.AccountLockManager { - return ds.accountLockManager + metadataManager datasource.MetadataManager + sysManager datasource.SystemManager + depManager datasource.DependencyManager + scManager datasource.SCManager + metricsManager datasource.MetricsManager } func (ds *DataSource) SystemManager() datasource.SystemManager { return ds.sysManager } -func (ds *DataSource) AccountManager() datasource.AccountManager { - return ds.accountManager -} - -func (ds *DataSource) RoleManager() datasource.RoleManager { - return ds.roleManager -} - func (ds *DataSource) DependencyManager() datasource.DependencyManager { return ds.depManager } @@ -89,10 +74,7 @@ func NewDataSource(opts datasource.Options) (datasource.DataSource, error) { inst.scManager = &SCManager{} inst.depManager = &DepManager{} inst.sysManager = &SysManager{} - inst.roleManager = &RoleManager{} inst.metadataManager = &MetadataManager{SchemaNotEditable: opts.SchemaNotEditable, InstanceTTL: opts.InstanceTTL} - inst.accountManager = &AccountManager{} - inst.accountLockManager = NewAccountLockManager() inst.metricsManager = &MetricsManager{} return inst, nil } diff --git a/datasource/mongo/role.go b/datasource/mongo/role.go index fbbb4ef29..a6b202239 100644 --- a/datasource/mongo/role.go +++ b/datasource/mongo/role.go @@ -22,28 +22,24 @@ import ( "strconv" "time" - "github.com/go-chassis/cari/rbac" - "go.mongodb.org/mongo-driver/bson" - - "github.com/apache/servicecomb-service-center/datasource" "github.com/apache/servicecomb-service-center/datasource/mongo/client" "github.com/apache/servicecomb-service-center/datasource/mongo/client/model" mutil "github.com/apache/servicecomb-service-center/datasource/mongo/util" + "github.com/apache/servicecomb-service-center/datasource/rbac" "github.com/apache/servicecomb-service-center/pkg/log" "github.com/apache/servicecomb-service-center/pkg/util" + rbacmodel "github.com/go-chassis/cari/rbac" + "go.mongodb.org/mongo-driver/bson" ) -type RoleManager struct { -} - -func (ds *RoleManager) CreateRole(ctx context.Context, r *rbac.Role) error { +func (ds *RbacDAO) CreateRole(ctx context.Context, r *rbacmodel.Role) error { exist, err := ds.RoleExist(ctx, r.Name) if err != nil { log.Error("failed to query role", err) return err } if exist { - return datasource.ErrRoleDuplicated + return rbac.ErrRoleDuplicated } r.ID = util.GenerateUUID() r.CreateTime = strconv.FormatInt(time.Now().Unix(), 10) @@ -51,7 +47,7 @@ func (ds *RoleManager) CreateRole(ctx context.Context, r *rbac.Role) error { _, err = client.GetMongoClient().Insert(ctx, model.CollectionRole, r) if err != nil { if client.IsDuplicateKey(err) { - return datasource.ErrRoleDuplicated + return rbac.ErrRoleDuplicated } return err } @@ -59,7 +55,7 @@ func (ds *RoleManager) CreateRole(ctx context.Context, r *rbac.Role) error { return nil } -func (ds *RoleManager) RoleExist(ctx context.Context, name string) (bool, error) { +func (ds *RbacDAO) RoleExist(ctx context.Context, name string) (bool, error) { filter := mutil.NewFilter(mutil.RoleName(name)) count, err := client.GetMongoClient().Count(ctx, model.CollectionRole, filter) if err != nil { @@ -71,16 +67,16 @@ func (ds *RoleManager) RoleExist(ctx context.Context, name string) (bool, error) return true, nil } -func (ds *RoleManager) GetRole(ctx context.Context, name string) (*rbac.Role, error) { +func (ds *RbacDAO) GetRole(ctx context.Context, name string) (*rbacmodel.Role, error) { filter := mutil.NewFilter(mutil.RoleName(name)) result, err := client.GetMongoClient().FindOne(ctx, model.CollectionRole, filter) if err != nil { return nil, err } if result.Err() != nil { - return nil, datasource.ErrRoleNotExist + return nil, rbac.ErrRoleNotExist } - var role rbac.Role + var role rbacmodel.Role err = result.Decode(&role) if err != nil { log.Error("failed to decode role", err) @@ -89,16 +85,16 @@ func (ds *RoleManager) GetRole(ctx context.Context, name string) (*rbac.Role, er return &role, nil } -func (ds *RoleManager) ListRole(ctx context.Context) ([]*rbac.Role, int64, error) { +func (ds *RbacDAO) ListRole(ctx context.Context) ([]*rbacmodel.Role, int64, error) { filter := mutil.NewFilter() cursor, err := client.GetMongoClient().Find(ctx, model.CollectionRole, filter) if err != nil { return nil, 0, err } - var roles []*rbac.Role + var roles []*rbacmodel.Role defer cursor.Close(ctx) for cursor.Next(ctx) { - var role rbac.Role + var role rbacmodel.Role err = cursor.Decode(&role) if err != nil { log.Error("failed to decode role", err) @@ -109,13 +105,13 @@ func (ds *RoleManager) ListRole(ctx context.Context) ([]*rbac.Role, int64, error return roles, int64(len(roles)), nil } -func (ds *RoleManager) DeleteRole(ctx context.Context, name string) (bool, error) { +func (ds *RbacDAO) DeleteRole(ctx context.Context, name string) (bool, error) { n, err := client.Count(ctx, model.CollectionAccount, bson.M{"roles": bson.M{"$in": []string{name}}}) if err != nil { return false, err } if n > 0 { - return false, datasource.ErrRoleBindingExist + return false, rbac.ErrRoleBindingExist } filter := mutil.NewFilter(mutil.RoleName(name)) result, err := client.DeleteDoc(ctx, model.CollectionRole, filter) @@ -128,7 +124,7 @@ func (ds *RoleManager) DeleteRole(ctx context.Context, name string) (bool, error return true, nil } -func (ds *RoleManager) UpdateRole(ctx context.Context, name string, role *rbac.Role) error { +func (ds *RbacDAO) UpdateRole(ctx context.Context, name string, role *rbacmodel.Role) error { filter := mutil.NewFilter(mutil.RoleName(name)) setFilter := mutil.NewFilter( mutil.ID(role.ID), diff --git a/datasource/account.go b/datasource/rbac/account.go similarity index 84% rename from datasource/account.go rename to datasource/rbac/account.go index d0235a05a..d46f19e07 100644 --- a/datasource/account.go +++ b/datasource/rbac/account.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package datasource +package rbac import ( "context" @@ -52,20 +52,20 @@ type AccountManager interface { UpdateAccount(ctx context.Context, name string, account *rbac.Account) error } -// AccountLockManager saves login failure status -type AccountLockManager interface { - UpsertLock(ctx context.Context, lock *AccountLock) error - GetLock(ctx context.Context, key string) (*AccountLock, error) - ListLock(ctx context.Context) ([]*AccountLock, int64, error) +// LockManager saves login failure status +type LockManager interface { + UpsertLock(ctx context.Context, lock *Lock) error + GetLock(ctx context.Context, key string) (*Lock, error) + ListLock(ctx context.Context) ([]*Lock, int64, error) DeleteLock(ctx context.Context, key string) error DeleteLockList(ctx context.Context, keys []string) error } -type AccountLock struct { +type Lock struct { Key string `json:"key,omitempty"` Status string `json:"status,omitempty"` ReleaseAt int64 `json:"releaseAt,omitempty" bson:"release_at"` } -type AccountLockResponse struct { - Total int64 `json:"total"` - AccountLock []*AccountLock `json:"data"` +type LockResponse struct { + Total int64 `json:"total"` + Locks []*Lock `json:"data"` } diff --git a/datasource/rbac/account_test.go b/datasource/rbac/account_test.go new file mode 100644 index 000000000..a15a5b3ac --- /dev/null +++ b/datasource/rbac/account_test.go @@ -0,0 +1,118 @@ +/* + * 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 rbac_test + +import ( + "context" + "strconv" + "testing" + "time" + + "github.com/apache/servicecomb-service-center/datasource/rbac" + rbacmodel "github.com/go-chassis/cari/rbac" + "github.com/stretchr/testify/assert" +) + +var ( + a1 = rbacmodel.Account{ + ID: "11111-22222-33333", + Name: "test-account1", + Password: "tnuocca-tset", + Roles: []string{"admin"}, + TokenExpirationTime: "2020-12-30", + CurrentPassword: "tnuocca-tset1", + } + a2 = rbacmodel.Account{ + ID: "11111-22222-33333-44444", + Name: "test-account2", + Password: "tnuocca-tset", + Roles: []string{"admin"}, + TokenExpirationTime: "2020-12-30", + CurrentPassword: "tnuocca-tset2", + } +) + +func TestAccount(t *testing.T) { + t.Run("add and get account", func(t *testing.T) { + err := rbac.Instance().CreateAccount(context.Background(), &a1) + assert.NoError(t, err) + err = rbac.Instance().CreateAccount(context.Background(), &a2) + assert.NoError(t, err) + r, err := rbac.Instance().GetAccount(context.Background(), a1.Name) + assert.NoError(t, err) + assert.Equal(t, a1, *r) + _, err = rbac.Instance().DeleteAccount(context.Background(), []string{a1.Name, a2.Name}) + assert.NoError(t, err) + }) + t.Run("account should exist", func(t *testing.T) { + err := rbac.Instance().CreateAccount(context.Background(), &a1) + assert.NoError(t, err) + exist, err := rbac.Instance().AccountExist(context.Background(), a1.Name) + assert.NoError(t, err) + assert.True(t, exist) + _, err = rbac.Instance().DeleteAccount(context.Background(), []string{a1.Name}) + assert.NoError(t, err) + }) + t.Run("delete account", func(t *testing.T) { + err := rbac.Instance().CreateAccount(context.Background(), &a2) + assert.NoError(t, err) + _, err = rbac.Instance().DeleteAccount(context.Background(), []string{a2.Name}) + assert.NoError(t, err) + }) + t.Run("add and update accounts then list", func(t *testing.T) { + err := rbac.Instance().CreateAccount(context.Background(), &a1) + assert.NoError(t, err) + err = rbac.Instance().CreateAccount(context.Background(), &a2) + assert.NoError(t, err) + a2.Password = "new-password" + err = rbac.Instance().UpdateAccount(context.Background(), a2.Name, &a2) + assert.NoError(t, err) + accounts, _, err := rbac.Instance().ListAccount(context.Background()) + assert.NoError(t, err) + assert.GreaterOrEqual(t, len(accounts), 2) + _, err = rbac.Instance().DeleteAccount(context.Background(), []string{a1.Name}) + assert.NoError(t, err) + _, err = rbac.Instance().DeleteAccount(context.Background(), []string{a2.Name}) + assert.NoError(t, err) + }) + t.Run("add and update accounts, should have create/update time", func(t *testing.T) { + err := rbac.Instance().CreateAccount(context.Background(), &a1) + assert.NoError(t, err) + + r, err := rbac.Instance().GetAccount(context.Background(), a1.Name) + assert.NoError(t, err) + dt, _ := strconv.Atoi(r.CreateTime) + assert.Less(t, 0, dt) + assert.Equal(t, r.CreateTime, r.UpdateTime) + + time.Sleep(time.Second) + a1.Password = "new-password" + err = rbac.Instance().UpdateAccount(context.Background(), a1.Name, &a1) + assert.NoError(t, err) + + old, _ := strconv.Atoi(r.UpdateTime) + r, err = rbac.Instance().GetAccount(context.Background(), a1.Name) + assert.NoError(t, err) + last, _ := strconv.Atoi(r.UpdateTime) + assert.Less(t, old, last) + assert.NotEqual(t, r.CreateTime, r.UpdateTime) + + _, err = rbac.Instance().DeleteAccount(context.Background(), []string{a1.Name}) + assert.NoError(t, err) + }) +} diff --git a/datasource/rbac/init.go b/datasource/rbac/init.go new file mode 100644 index 000000000..be5652e9c --- /dev/null +++ b/datasource/rbac/init.go @@ -0,0 +1,62 @@ +/* + * 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 rbac + +import ( + "fmt" + + "github.com/apache/servicecomb-service-center/pkg/log" +) + +type initFunc func(opts Options) (DAO, error) + +var ( + plugins = make(map[string]initFunc) + instance DAO +) + +// Install load plugins configuration into plugins +func Install(pluginImplName string, f initFunc) { + plugins[pluginImplName] = f +} + +// Init construct storage plugin instance +// invoked by sc main process. +func Init(opts Options) error { + if opts.Kind == "" { + return nil + } + + engineFunc, ok := plugins[opts.Kind] + if !ok { + return fmt.Errorf("plugin implement not supported [%s]", opts.Kind) + } + + var err error + instance, err = engineFunc(opts) + if err != nil { + return err + } + log.Info(fmt.Sprintf("rbac plugin [%s] enabled", opts.Kind)) + + return nil +} + +func Instance() DAO { + return instance +} diff --git a/datasource/rbac/options.go b/datasource/rbac/options.go new file mode 100644 index 000000000..69573c39c --- /dev/null +++ b/datasource/rbac/options.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 rbac + +//Options contains configuration for plugins +type Options struct { + Kind string +} diff --git a/datasource/rbac/rbac.go b/datasource/rbac/rbac.go new file mode 100644 index 000000000..b9d5c32dc --- /dev/null +++ b/datasource/rbac/rbac.go @@ -0,0 +1,24 @@ +/* + * 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 rbac + +type DAO interface { + AccountManager + RoleManager + LockManager +} diff --git a/datasource/role.go b/datasource/rbac/role.go similarity index 98% rename from datasource/role.go rename to datasource/rbac/role.go index 076f6d626..c06dd09de 100644 --- a/datasource/role.go +++ b/datasource/rbac/role.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package datasource +package rbac import ( "context" diff --git a/datasource/role_test.go b/datasource/rbac/role_test.go similarity index 63% rename from datasource/role_test.go rename to datasource/rbac/role_test.go index 5ed6259e7..13fc69104 100644 --- a/datasource/role_test.go +++ b/datasource/rbac/role_test.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package datasource_test +package rbac_test import ( "context" @@ -23,27 +23,25 @@ import ( "testing" "time" - "github.com/go-chassis/cari/rbac" - + "github.com/apache/servicecomb-service-center/datasource/rbac" + rbacmodel "github.com/go-chassis/cari/rbac" "github.com/stretchr/testify/assert" - - "github.com/apache/servicecomb-service-center/datasource" ) var ( - r1 = rbac.Role{ + r1 = rbacmodel.Role{ ID: "11111-22222-33333", Name: "test-role", Perms: nil, } - r2 = rbac.Role{ + r2 = rbacmodel.Role{ ID: "11111-22222-33333-44444", Name: "test-role-ex", Perms: nil, } - a = rbac.Account{ + a = rbacmodel.Account{ Name: "account-role-test", Password: "abc", Roles: []string{"test-role"}, @@ -52,9 +50,9 @@ var ( func TestRole(t *testing.T) { t.Run("create role should success", func(t *testing.T) { - err := datasource.GetRoleManager().CreateRole(context.Background(), &r1) + err := rbac.Instance().CreateRole(context.Background(), &r1) assert.NoError(t, err) - r, err := datasource.GetRoleManager().GetRole(context.Background(), "test-role") + r, err := rbac.Instance().GetRole(context.Background(), "test-role") assert.NoError(t, err) assert.Equal(t, r1, *r) dt, _ := strconv.Atoi(r.CreateTime) @@ -62,27 +60,27 @@ func TestRole(t *testing.T) { assert.Equal(t, r.CreateTime, r.UpdateTime) }) t.Run("role should exist", func(t *testing.T) { - exist, err := datasource.GetRoleManager().RoleExist(context.Background(), "test-role") + exist, err := rbac.Instance().RoleExist(context.Background(), "test-role") assert.NoError(t, err) assert.True(t, exist) }) t.Run("repeated create role should failed", func(t *testing.T) { - err := datasource.GetRoleManager().CreateRole(context.Background(), &r1) + err := rbac.Instance().CreateRole(context.Background(), &r1) assert.Error(t, err) }) t.Run("update role should success", func(t *testing.T) { - r, err := datasource.GetRoleManager().GetRole(context.Background(), "test-role") + r, err := rbac.Instance().GetRole(context.Background(), "test-role") assert.NoError(t, err) old, _ := strconv.Atoi(r.UpdateTime) time.Sleep(time.Second) r1.ID = "11111-22222-33333-4" - err = datasource.GetRoleManager().UpdateRole(context.Background(), "test-role", &r1) + err = rbac.Instance().UpdateRole(context.Background(), "test-role", &r1) assert.NoError(t, err) - r, err = datasource.GetRoleManager().GetRole(context.Background(), "test-role") + r, err = rbac.Instance().GetRole(context.Background(), "test-role") assert.NoError(t, err) last, _ := strconv.Atoi(r.UpdateTime) assert.Less(t, old, last) @@ -90,39 +88,39 @@ func TestRole(t *testing.T) { }) t.Run("add new role should success", func(t *testing.T) { - err := datasource.GetRoleManager().CreateRole(context.Background(), &r2) + err := rbac.Instance().CreateRole(context.Background(), &r2) assert.NoError(t, err) - _, n, err := datasource.GetRoleManager().ListRole(context.Background()) + _, n, err := rbac.Instance().ListRole(context.Background()) assert.NoError(t, err) assert.Equal(t, int64(2), n) }) t.Run("delete role bind to user should failed", func(t *testing.T) { - err := datasource.GetAccountManager().CreateAccount(context.Background(), &a) + err := rbac.Instance().CreateAccount(context.Background(), &a) assert.NoError(t, err) - _, err = datasource.GetRoleManager().DeleteRole(context.Background(), "test-role") + _, err = rbac.Instance().DeleteRole(context.Background(), "test-role") assert.Error(t, err) }) t.Run("update account role and delete old role should success", func(t *testing.T) { a.Roles = []string{"test-role-ex"} - err := datasource.GetAccountManager().UpdateAccount(context.Background(), "account-role-test", &a) + err := rbac.Instance().UpdateAccount(context.Background(), "account-role-test", &a) assert.NoError(t, err) - _, err = datasource.GetRoleManager().DeleteRole(context.Background(), "test-role") + _, err = rbac.Instance().DeleteRole(context.Background(), "test-role") assert.NoError(t, err) }) t.Run("delete role should success", func(t *testing.T) { - b, err := datasource.GetAccountManager().DeleteAccount(context.Background(), []string{"account-role-test"}) + b, err := rbac.Instance().DeleteAccount(context.Background(), []string{"account-role-test"}) assert.True(t, b) assert.NoError(t, err) - _, err = datasource.GetRoleManager().DeleteRole(context.Background(), "test-role-ex") + _, err = rbac.Instance().DeleteRole(context.Background(), "test-role-ex") assert.NoError(t, err) - _, n, err := datasource.GetRoleManager().ListRole(context.Background()) + _, n, err := rbac.Instance().ListRole(context.Background()) assert.NoError(t, err) assert.Equal(t, int64(0), n) }) diff --git a/datasource/schema/init.go b/datasource/schema/init.go index 2a6e6d1a6..1c24df50e 100644 --- a/datasource/schema/init.go +++ b/datasource/schema/init.go @@ -23,16 +23,16 @@ import ( "github.com/apache/servicecomb-service-center/pkg/log" ) -type engine func(opts Options) (DAO, error) +type initFunc func(opts Options) (DAO, error) var ( - plugins = make(map[string]engine) + plugins = make(map[string]initFunc) instance DAO ) // Install load plugins configuration into plugins -func Install(pluginImplName string, engineFunc engine) { - plugins[pluginImplName] = engineFunc +func Install(pluginImplName string, f initFunc) { + plugins[pluginImplName] = f } // Init construct storage plugin instance diff --git a/server/job/account/account_test.go b/server/job/account/account_test.go index 1c6c32258..bc76ddf08 100644 --- a/server/job/account/account_test.go +++ b/server/job/account/account_test.go @@ -24,7 +24,7 @@ import ( _ "github.com/apache/servicecomb-service-center/test" - "github.com/apache/servicecomb-service-center/datasource" + dao "github.com/apache/servicecomb-service-center/datasource/rbac" "github.com/apache/servicecomb-service-center/server/job/account" "github.com/stretchr/testify/assert" ) @@ -32,9 +32,9 @@ import ( func TestCleanupReleasedLockHistory(t *testing.T) { t.Run("have a released lock, should be cleanup", func(t *testing.T) { const key = "TestCleanupReleasedLockHistory1" - err := datasource.GetAccountLockManager().UpsertLock(context.Background(), &datasource.AccountLock{ + err := dao.Instance().UpsertLock(context.Background(), &dao.Lock{ Key: key, - Status: datasource.StatusBanned, + Status: dao.StatusBanned, ReleaseAt: time.Now().Add(-time.Second).Unix(), }) assert.NoError(t, err) @@ -42,14 +42,14 @@ func TestCleanupReleasedLockHistory(t *testing.T) { err = account.CleanupReleasedLockHistory(context.Background()) assert.NoError(t, err) - _, err = datasource.GetAccountLockManager().GetLock(context.Background(), key) - assert.Equal(t, datasource.ErrAccountLockNotExist, err) + _, err = dao.Instance().GetLock(context.Background(), key) + assert.Equal(t, dao.ErrAccountLockNotExist, err) }) t.Run("have an unreleased lock, should NOT be cleanup", func(t *testing.T) { const key = "TestCleanupReleasedLockHistory2" - err := datasource.GetAccountLockManager().UpsertLock(context.Background(), &datasource.AccountLock{ + err := dao.Instance().UpsertLock(context.Background(), &dao.Lock{ Key: key, - Status: datasource.StatusBanned, + Status: dao.StatusBanned, ReleaseAt: time.Now().Add(time.Minute).Unix(), }) assert.NoError(t, err) @@ -57,11 +57,11 @@ func TestCleanupReleasedLockHistory(t *testing.T) { err = account.CleanupReleasedLockHistory(context.Background()) assert.NoError(t, err) - lock, err := datasource.GetAccountLockManager().GetLock(context.Background(), key) + lock, err := dao.Instance().GetLock(context.Background(), key) assert.NoError(t, err) assert.NotNil(t, lock) - err = datasource.GetAccountLockManager().DeleteLock(context.Background(), key) + err = dao.Instance().DeleteLock(context.Background(), key) assert.NoError(t, err) }) } diff --git a/server/plugin/quota/buildin/account.go b/server/plugin/quota/buildin/account.go index 3fa5f42f5..a5ec0af6b 100644 --- a/server/plugin/quota/buildin/account.go +++ b/server/plugin/quota/buildin/account.go @@ -20,11 +20,11 @@ package buildin import ( "context" - "github.com/apache/servicecomb-service-center/datasource" + "github.com/apache/servicecomb-service-center/datasource/rbac" ) func AccountUsage(ctx context.Context) (int64, error) { - _, used, err := datasource.GetAccountManager().ListAccount(ctx) + _, used, err := rbac.Instance().ListAccount(ctx) if err != nil { return 0, err } diff --git a/server/plugin/quota/buildin/role.go b/server/plugin/quota/buildin/role.go index f771d141e..c7cb44f65 100644 --- a/server/plugin/quota/buildin/role.go +++ b/server/plugin/quota/buildin/role.go @@ -20,11 +20,11 @@ package buildin import ( "context" - "github.com/apache/servicecomb-service-center/datasource" + "github.com/apache/servicecomb-service-center/datasource/rbac" ) func RoleUsage(ctx context.Context) (int64, error) { - _, used, err := datasource.GetRoleManager().ListRole(ctx) + _, used, err := rbac.Instance().ListRole(ctx) if err != nil { return 0, err } diff --git a/server/resource/rbac/auth_resource.go b/server/resource/rbac/auth_resource.go index ab3046960..89249a1ca 100644 --- a/server/resource/rbac/auth_resource.go +++ b/server/resource/rbac/auth_resource.go @@ -22,7 +22,7 @@ import ( "io/ioutil" "net/http" - "github.com/apache/servicecomb-service-center/datasource" + "github.com/apache/servicecomb-service-center/datasource/rbac" 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" @@ -30,7 +30,7 @@ import ( rbacsvc "github.com/apache/servicecomb-service-center/server/service/rbac" "github.com/apache/servicecomb-service-center/server/service/validator" "github.com/go-chassis/cari/discovery" - "github.com/go-chassis/cari/rbac" + rbacmodel "github.com/go-chassis/cari/rbac" "github.com/go-chassis/go-chassis/v2/security/authr" ) @@ -61,7 +61,7 @@ func (ar *AuthResource) CreateAccount(w http.ResponseWriter, req *http.Request) rest.WriteError(w, discovery.ErrInternal, err.Error()) return } - a := &rbac.Account{} + a := &rbacmodel.Account{} if err = json.Unmarshal(body, a); err != nil { log.Error("json err", err) rest.WriteError(w, discovery.ErrInvalidParams, err.Error()) @@ -95,7 +95,7 @@ func (ar *AuthResource) UpdateAccount(w http.ResponseWriter, req *http.Request) rest.WriteError(w, discovery.ErrInternal, err.Error()) return } - a := &rbac.Account{} + a := &rbacmodel.Account{} if err = json.Unmarshal(body, a); err != nil { log.Error("json err", err) rest.WriteError(w, discovery.ErrInvalidParams, err.Error()) @@ -118,7 +118,7 @@ func (ar *AuthResource) ListAccount(w http.ResponseWriter, r *http.Request) { rest.WriteError(w, discovery.ErrInternal, errorsEx.MsgGetAccountFailed) return } - resp := &rbac.AccountResponse{ + resp := &rbacmodel.AccountResponse{ Total: n, Accounts: as, } @@ -143,7 +143,7 @@ func (ar *AuthResource) ChangePassword(w http.ResponseWriter, req *http.Request) rest.WriteError(w, discovery.ErrInternal, err.Error()) return } - a := &rbac.Account{} + a := &rbacmodel.Account{} if err = json.Unmarshal(body, a); err != nil { log.Error("json err", err) rest.WriteError(w, discovery.ErrInvalidParams, errorsEx.MsgJSON) @@ -166,7 +166,7 @@ func (ar *AuthResource) Login(w http.ResponseWriter, r *http.Request) { rest.WriteError(w, discovery.ErrInternal, err.Error()) return } - a := &rbac.Account{} + a := &rbacmodel.Account{} if err = json.Unmarshal(body, a); err != nil { log.Error("json err", err) rest.WriteError(w, discovery.ErrInvalidParams, err.Error()) @@ -187,7 +187,7 @@ func (ar *AuthResource) Login(w http.ResponseWriter, r *http.Request) { rest.WriteServiceError(w, err) return } - rest.WriteResponse(w, r, nil, &rbac.Token{TokenStr: t}) + rest.WriteResponse(w, r, nil, &rbacmodel.Token{TokenStr: t}) } func (ar *AuthResource) ListSelfPerms(w http.ResponseWriter, r *http.Request) { @@ -197,7 +197,7 @@ func (ar *AuthResource) ListSelfPerms(w http.ResponseWriter, r *http.Request) { rest.WriteServiceError(w, err) return } - resp := &rbac.SelfPermissionResponse{ + resp := &rbacmodel.SelfPermissionResponse{ Perms: perms, } rest.WriteResponse(w, r, nil, resp) @@ -211,9 +211,9 @@ func (ar *AuthResource) ListLock(w http.ResponseWriter, r *http.Request) { rest.WriteServiceError(w, err) return } - resp := &datasource.AccountLockResponse{ - Total: n, - AccountLock: al, + resp := &rbac.LockResponse{ + Total: n, + Locks: al, } rest.WriteResponse(w, r, nil, resp) } diff --git a/server/resource/rbac/auth_resource_test.go b/server/resource/rbac/auth_resource_test.go index ac8a5bd79..8ba3cc6f3 100644 --- a/server/resource/rbac/auth_resource_test.go +++ b/server/resource/rbac/auth_resource_test.go @@ -27,9 +27,9 @@ import ( "testing" "time" + "github.com/apache/servicecomb-service-center/datasource/rbac" _ "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/server/config" v4 "github.com/apache/servicecomb-service-center/server/resource/rbac" @@ -442,7 +442,7 @@ func TestAuthResource_ListLock(t *testing.T) { w3 := httptest.NewRecorder() rest.GetRouter().ServeHTTP(w3, r3) assert.Equal(t, http.StatusOK, w3.Code) - resp := &datasource.AccountLockResponse{} + resp := &rbac.LockResponse{} err := json.Unmarshal(w.Body.Bytes(), resp) assert.NoError(t, err) }) diff --git a/server/service/account/account.go b/server/service/account/account.go index 36a633692..40ecdef10 100644 --- a/server/service/account/account.go +++ b/server/service/account/account.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/apache/servicecomb-service-center/datasource" + "github.com/apache/servicecomb-service-center/datasource/rbac" "github.com/apache/servicecomb-service-center/pkg/log" "github.com/apache/servicecomb-service-center/server/config" ) @@ -18,7 +18,7 @@ const ( func IsBanned(ctx context.Context, key string) (bool, error) { lock, err := GetLock(ctx, key) if err != nil { - if err == datasource.ErrAccountLockNotExist { + if err == rbac.ErrAccountLockNotExist { return false, nil } return false, err @@ -27,46 +27,46 @@ func IsBanned(ctx context.Context, key string) (bool, error) { err = DeleteLock(ctx, key) if err != nil { log.Error("remove lock failed", err) - return false, datasource.ErrCannotReleaseLock + return false, rbac.ErrCannotReleaseLock } log.Info(fmt.Sprintf("release lock for %s", key)) return false, nil } - if lock.Status == datasource.StatusBanned { + if lock.Status == rbac.StatusBanned { return true, nil } return false, nil } func Ban(ctx context.Context, key string) error { - return Lock(ctx, key, datasource.StatusBanned) + return Lock(ctx, key, rbac.StatusBanned) } func Lock(ctx context.Context, key, status string) error { duration := config.GetDuration("rbac.retainLockHistoryFor", defaultRetainLockHistoryFor) - if status == datasource.StatusBanned { + if status == rbac.StatusBanned { duration = config.GetDuration("rbac.releaseLockAfter", defaultReleaseLockAfter) } - lock := &datasource.AccountLock{ + lock := &rbac.Lock{ Key: key, Status: status, ReleaseAt: time.Now().Add(duration).Unix(), } - return datasource.GetAccountLockManager().UpsertLock(ctx, lock) + return rbac.Instance().UpsertLock(ctx, lock) } -func GetLock(ctx context.Context, key string) (*datasource.AccountLock, error) { - return datasource.GetAccountLockManager().GetLock(ctx, key) +func GetLock(ctx context.Context, key string) (*rbac.Lock, error) { + return rbac.Instance().GetLock(ctx, key) } -func ListLock(ctx context.Context) ([]*datasource.AccountLock, int64, error) { - return datasource.GetAccountLockManager().ListLock(ctx) +func ListLock(ctx context.Context) ([]*rbac.Lock, int64, error) { + return rbac.Instance().ListLock(ctx) } func DeleteLock(ctx context.Context, key string) error { - return datasource.GetAccountLockManager().DeleteLock(ctx, key) + return rbac.Instance().DeleteLock(ctx, key) } func DeleteLockList(ctx context.Context, keys []string) error { - return datasource.GetAccountLockManager().DeleteLockList(ctx, keys) + return rbac.Instance().DeleteLockList(ctx, keys) } diff --git a/server/service/account/account_test.go b/server/service/account/account_test.go index 2c18e9d79..6b33e4be1 100644 --- a/server/service/account/account_test.go +++ b/server/service/account/account_test.go @@ -7,27 +7,27 @@ import ( _ "github.com/apache/servicecomb-service-center/test" - "github.com/apache/servicecomb-service-center/datasource" - "github.com/apache/servicecomb-service-center/server/service/account" + dao "github.com/apache/servicecomb-service-center/datasource/rbac" + accountsvc "github.com/apache/servicecomb-service-center/server/service/account" "github.com/stretchr/testify/assert" ) func TestIsBanned(t *testing.T) { t.Run("ban a key and check status, it should be banned, check other key should not be banned", func(t *testing.T) { - err := account.Ban(context.TODO(), "dev_guy::127.0.0.1") + err := accountsvc.Ban(context.TODO(), "dev_guy::127.0.0.1") assert.NoError(t, err) - ok, err := account.IsBanned(context.TODO(), "dev_guy::127.0.0.1") + ok, err := accountsvc.IsBanned(context.TODO(), "dev_guy::127.0.0.1") assert.NoError(t, err) assert.True(t, ok) - ok, err = account.IsBanned(context.TODO(), "test_guy::127.0.0.1") + ok, err = accountsvc.IsBanned(context.TODO(), "test_guy::127.0.0.1") assert.NoError(t, err) assert.False(t, ok) time.Sleep(4 * time.Second) - ok, err = account.IsBanned(context.TODO(), "dev_guy::127.0.0.1") + ok, err = accountsvc.IsBanned(context.TODO(), "dev_guy::127.0.0.1") assert.NoError(t, err) assert.False(t, ok) }) @@ -35,10 +35,10 @@ func TestIsBanned(t *testing.T) { func TestListLock(t *testing.T) { t.Run("list 1 account lock, should return 1 item", func(t *testing.T) { - err := account.Ban(context.TODO(), "dev_lock::127.0.0.1") + err := accountsvc.Ban(context.TODO(), "dev_lock::127.0.0.1") assert.NoError(t, err) - locks, n, err := account.ListLock(context.Background()) + locks, n, err := accountsvc.ListLock(context.Background()) assert.NoError(t, err) assert.NotEqual(t, 0, n) for _, lock := range locks { @@ -54,12 +54,12 @@ func TestBan(t *testing.T) { var banTime int64 t.Run("ban account TestAccountLock, should return no error", func(t *testing.T) { - err := account.Ban(context.Background(), "TestAccountLock") + err := accountsvc.Ban(context.Background(), "TestAccountLock") assert.NoError(t, err) - lock, err := datasource.GetAccountLockManager().GetLock(context.Background(), "TestAccountLock") + lock, err := dao.Instance().GetLock(context.Background(), "TestAccountLock") assert.NoError(t, err) - assert.Equal(t, datasource.StatusBanned, lock.Status) + assert.Equal(t, dao.StatusBanned, lock.Status) assert.Less(t, time.Now().Unix(), lock.ReleaseAt) banTime = lock.ReleaseAt @@ -68,35 +68,35 @@ func TestBan(t *testing.T) { t.Run("ban account TestAccountLock again, should return a new release time", func(t *testing.T) { time.Sleep(time.Second) - err := account.Ban(context.Background(), "TestAccountLock") + err := accountsvc.Ban(context.Background(), "TestAccountLock") assert.NoError(t, err) - lock, err := datasource.GetAccountLockManager().GetLock(context.Background(), "TestAccountLock") + lock, err := dao.Instance().GetLock(context.Background(), "TestAccountLock") assert.NoError(t, err) - assert.Equal(t, datasource.StatusBanned, lock.Status) + assert.Equal(t, dao.StatusBanned, lock.Status) assert.Less(t, banTime, lock.ReleaseAt) }) t.Run("ban account TestAccountLock again, should refresh releaseAt", func(t *testing.T) { - lock1, err := datasource.GetAccountLockManager().GetLock(context.Background(), "TestAccountLock") + lock1, err := dao.Instance().GetLock(context.Background(), "TestAccountLock") assert.NoError(t, err) - assert.Equal(t, datasource.StatusBanned, lock1.Status) + assert.Equal(t, dao.StatusBanned, lock1.Status) time.Sleep(time.Second) - err = account.Ban(context.Background(), "TestAccountLock") + err = accountsvc.Ban(context.Background(), "TestAccountLock") assert.NoError(t, err) - lock2, err := datasource.GetAccountLockManager().GetLock(context.Background(), "TestAccountLock") + lock2, err := dao.Instance().GetLock(context.Background(), "TestAccountLock") assert.NoError(t, err) assert.Less(t, lock1.ReleaseAt, lock2.ReleaseAt) }) t.Run("delete account lock, should return no error", func(t *testing.T) { - err := datasource.GetAccountLockManager().DeleteLock(context.Background(), "TestAccountLock") + err := dao.Instance().DeleteLock(context.Background(), "TestAccountLock") assert.NoError(t, err) - lock, err := datasource.GetAccountLockManager().GetLock(context.Background(), "TestAccountLock") - assert.Equal(t, datasource.ErrAccountLockNotExist, err) + lock, err := dao.Instance().GetLock(context.Background(), "TestAccountLock") + assert.Equal(t, dao.ErrAccountLockNotExist, err) assert.Nil(t, lock) }) } diff --git a/server/service/rbac/account_service.go b/server/service/rbac/account_service.go index f893f37db..04d0fa44b 100644 --- a/server/service/rbac/account_service.go +++ b/server/service/rbac/account_service.go @@ -22,20 +22,20 @@ import ( "context" "fmt" - "github.com/apache/servicecomb-service-center/datasource" + "github.com/apache/servicecomb-service-center/datasource/rbac" errorsEx "github.com/apache/servicecomb-service-center/pkg/errors" "github.com/apache/servicecomb-service-center/pkg/log" quotasvc "github.com/apache/servicecomb-service-center/server/service/quota" "github.com/apache/servicecomb-service-center/server/service/validator" "github.com/go-chassis/cari/discovery" - "github.com/go-chassis/cari/rbac" + rbacmodel "github.com/go-chassis/cari/rbac" ) //CreateAccount save account info -func CreateAccount(ctx context.Context, a *rbac.Account) error { +func CreateAccount(ctx context.Context, a *rbacmodel.Account) error { quotaErr := quotasvc.ApplyAccount(ctx, 1) if quotaErr != nil { - return rbac.NewError(rbac.ErrAccountNoQuota, quotaErr.Error()) + return rbacmodel.NewError(rbacmodel.ErrAccountNoQuota, quotaErr.Error()) } err := validator.ValidateCreateAccount(a) if err != nil { @@ -51,23 +51,23 @@ func CreateAccount(ctx context.Context, a *rbac.Account) error { return discovery.NewError(discovery.ErrInvalidParams, err.Error()) } if err = checkRoleNames(ctx, a.Roles); err != nil { - return rbac.NewError(rbac.ErrAccountHasInvalidRole, err.Error()) + return rbacmodel.NewError(rbacmodel.ErrAccountHasInvalidRole, err.Error()) } - err = datasource.GetAccountManager().CreateAccount(ctx, a) + err = rbac.Instance().CreateAccount(ctx, a) if err == nil { log.Info(fmt.Sprintf("create account [%s] success", a.Name)) return nil } log.Error(fmt.Sprintf("create account [%s] failed", a.Name), err) - if err == datasource.ErrAccountDuplicated { - return rbac.NewError(rbac.ErrAccountConflict, err.Error()) + if err == rbac.ErrAccountDuplicated { + return rbacmodel.NewError(rbacmodel.ErrAccountConflict, err.Error()) } return err } // UpdateAccount updates an account's info, except the password -func UpdateAccount(ctx context.Context, name string, a *rbac.Account) error { +func UpdateAccount(ctx context.Context, name string, a *rbacmodel.Account) error { // todo params validation err := validator.ValidateUpdateAccount(a) if err != nil { @@ -92,9 +92,9 @@ func UpdateAccount(ctx context.Context, name string, a *rbac.Account) error { oldAccount.Roles = a.Roles } if err = checkRoleNames(ctx, oldAccount.Roles); err != nil { - return rbac.NewError(rbac.ErrAccountHasInvalidRole, err.Error()) + return rbacmodel.NewError(rbacmodel.ErrAccountHasInvalidRole, err.Error()) } - err = datasource.GetAccountManager().UpdateAccount(ctx, name, oldAccount) + err = rbac.Instance().UpdateAccount(ctx, name, oldAccount) if err != nil { log.Error("can not edit account info", err) return err @@ -103,52 +103,52 @@ func UpdateAccount(ctx context.Context, name string, a *rbac.Account) error { return nil } -func GetAccount(ctx context.Context, name string) (*rbac.Account, error) { - r, err := datasource.GetAccountManager().GetAccount(ctx, name) +func GetAccount(ctx context.Context, name string) (*rbacmodel.Account, error) { + r, err := rbac.Instance().GetAccount(ctx, name) if err != nil { - if err == datasource.ErrAccountNotExist { + if err == rbac.ErrAccountNotExist { msg := fmt.Sprintf("account [%s] not exist", name) - return nil, rbac.NewError(rbac.ErrAccountNotExist, msg) + return nil, rbacmodel.NewError(rbacmodel.ErrAccountNotExist, msg) } return nil, err } return r, nil } -func ListAccount(ctx context.Context) ([]*rbac.Account, int64, error) { - return datasource.GetAccountManager().ListAccount(ctx) +func ListAccount(ctx context.Context) ([]*rbacmodel.Account, int64, error) { + return rbac.Instance().ListAccount(ctx) } func AccountExist(ctx context.Context, name string) (bool, error) { - return datasource.GetAccountManager().AccountExist(ctx, name) + return rbac.Instance().AccountExist(ctx, name) } func DeleteAccount(ctx context.Context, name string) error { if err := illegalAccountCheck(ctx, name); err != nil { return err } - exist, err := datasource.GetAccountManager().AccountExist(ctx, name) + exist, err := rbac.Instance().AccountExist(ctx, name) if err != nil { log.Error(fmt.Sprintf("check account [%s] exit failed", name), err) return err } if !exist { msg := fmt.Sprintf("account [%s] not exist", name) - return rbac.NewError(rbac.ErrAccountNotExist, msg) + return rbacmodel.NewError(rbacmodel.ErrAccountNotExist, msg) } - _, err = datasource.GetAccountManager().DeleteAccount(ctx, []string{name}) + _, err = rbac.Instance().DeleteAccount(ctx, []string{name}) return err } //EditAccount save account info -func EditAccount(ctx context.Context, a *rbac.Account) error { - exist, err := datasource.GetAccountManager().AccountExist(ctx, a.Name) +func EditAccount(ctx context.Context, a *rbacmodel.Account) error { + exist, err := rbac.Instance().AccountExist(ctx, a.Name) if err != nil { log.Error("can not edit account info", err) return err } if !exist { - return rbac.NewError(rbac.ErrAccountNotExist, "") + return rbacmodel.NewError(rbacmodel.ErrAccountNotExist, "") } - err = datasource.GetAccountManager().UpdateAccount(ctx, a.Name, a) + err = rbac.Instance().UpdateAccount(ctx, a.Name, a) if err != nil { log.Error("can not edit account info", err) return err @@ -165,7 +165,7 @@ func checkRoleNames(ctx context.Context, roles []string) error { return err } if !exist { - return datasource.ErrRoleNotExist + return rbac.ErrRoleNotExist } } return nil @@ -173,11 +173,11 @@ func checkRoleNames(ctx context.Context, roles []string) error { func illegalAccountCheck(ctx context.Context, target string) error { if target == RootName { - return rbac.NewError(rbac.ErrForbidOperateBuildInAccount, errorsEx.MsgCantOperateRoot) + return rbacmodel.NewError(rbacmodel.ErrForbidOperateBuildInAccount, errorsEx.MsgCantOperateRoot) } changer := UserFromContext(ctx) if target == changer { - return rbac.NewError(rbac.ErrForbidOperateSelfAccount, errorsEx.MsgCantOperateYour) + return rbacmodel.NewError(rbacmodel.ErrForbidOperateSelfAccount, errorsEx.MsgCantOperateYour) } return nil } diff --git a/server/service/rbac/blocker.go b/server/service/rbac/blocker.go index 6f6d943d4..365abe57c 100644 --- a/server/service/rbac/blocker.go +++ b/server/service/rbac/blocker.go @@ -23,7 +23,7 @@ import ( "sync" "time" - "github.com/apache/servicecomb-service-center/datasource" + "github.com/apache/servicecomb-service-center/datasource/rbac" "github.com/apache/servicecomb-service-center/pkg/log" accountsvc "github.com/apache/servicecomb-service-center/server/service/account" "golang.org/x/time/rate" @@ -59,9 +59,9 @@ func TryLockAccount(key string) { } allow := l.limiter.AllowN(time.Now(), 1) - status := datasource.StatusAttempted + status := rbac.StatusAttempted if !allow { - status = datasource.StatusBanned + status = rbac.StatusBanned } err := accountsvc.Lock(context.Background(), key, status) if err != nil { diff --git a/server/service/rbac/blocker_test.go b/server/service/rbac/blocker_test.go index eb9e80151..b4d9b3ed6 100644 --- a/server/service/rbac/blocker_test.go +++ b/server/service/rbac/blocker_test.go @@ -22,7 +22,7 @@ import ( "testing" "time" - "github.com/apache/servicecomb-service-center/datasource" + dao "github.com/apache/servicecomb-service-center/datasource/rbac" "github.com/apache/servicecomb-service-center/server/resource/rbac" accountsvc "github.com/apache/servicecomb-service-center/server/service/account" rbacsvc "github.com/apache/servicecomb-service-center/server/service/rbac" @@ -61,9 +61,9 @@ func TestCountFailure(t *testing.T) { assert.False(t, rbacsvc.IsBanned(key2)) _, err := accountsvc.GetLock(context.Background(), key1) - assert.ErrorIs(t, datasource.ErrAccountLockNotExist, err) + assert.ErrorIs(t, dao.ErrAccountLockNotExist, err) _, err = accountsvc.GetLock(context.Background(), key2) - assert.ErrorIs(t, datasource.ErrAccountLockNotExist, err) + assert.ErrorIs(t, dao.ErrAccountLockNotExist, err) }) } @@ -75,7 +75,7 @@ func TestTryLockAccount(t *testing.T) { lock, err := accountsvc.GetLock(context.Background(), key1) assert.NoError(t, err) - assert.Equal(t, datasource.StatusAttempted, lock.Status) + assert.Equal(t, dao.StatusAttempted, lock.Status) assert.False(t, rbacsvc.IsBanned(key1)) @@ -89,7 +89,7 @@ func TestTryLockAccount(t *testing.T) { lock, err := accountsvc.GetLock(context.Background(), key1) assert.NoError(t, err) - assert.Equal(t, datasource.StatusAttempted, lock.Status) + assert.Equal(t, dao.StatusAttempted, lock.Status) assert.Less(t, oldReleaseAt, lock.ReleaseAt) assert.False(t, rbacsvc.IsBanned(key1)) diff --git a/server/service/rbac/decision.go b/server/service/rbac/decision.go index 96b164942..4a8257796 100644 --- a/server/service/rbac/decision.go +++ b/server/service/rbac/decision.go @@ -21,10 +21,10 @@ import ( "context" "fmt" - "github.com/apache/servicecomb-service-center/datasource" + "github.com/apache/servicecomb-service-center/datasource/rbac" "github.com/apache/servicecomb-service-center/pkg/log" "github.com/apache/servicecomb-service-center/server/plugin/auth" - "github.com/go-chassis/cari/rbac" + rbacmodel "github.com/go-chassis/cari/rbac" ) // Allow return: matched labels(empty if no label defined), error @@ -38,11 +38,11 @@ func Allow(ctx context.Context, project string, roleList []string, } if len(allPerms) == 0 { log.Warn("role list has no any permissions") - return nil, rbac.NewError(rbac.ErrNoPermission, "role has no any permissions") + return nil, rbacmodel.NewError(rbacmodel.ErrNoPermission, "role has no any permissions") } allow, labelList := GetLabel(allPerms, targetResource.Type, targetResource.Verb) if !allow { - return nil, rbac.NewError(rbac.ErrNoPermission, + return nil, rbacmodel.NewError(rbacmodel.ErrNoPermission, fmt.Sprintf("role has no permissions[%s:%s]", targetResource.Type, targetResource.Verb)) } // allow, but no label found, means we can ignore the labels @@ -57,7 +57,7 @@ func Allow(ctx context.Context, project string, roleList []string, filteredLabelList := FilterLabel(targetResource.Labels, labelList) // target resource label matches no label in permission, means not allow if len(filteredLabelList) == 0 { - return nil, rbac.NewError(rbac.ErrNoPermission, + return nil, rbacmodel.NewError(rbacmodel.ErrNoPermission, fmt.Sprintf("role has no permissions[%s:%s] for labels %v", targetResource.Type, targetResource.Verb, targetResource.Labels)) } @@ -85,15 +85,15 @@ func LabelMatched(targetResourceLabel map[string]string, permLabel map[string]st return true } -func getPermsByRoles(ctx context.Context, roleList []string) ([]*rbac.Permission, error) { - var allPerms = make([]*rbac.Permission, 0) +func getPermsByRoles(ctx context.Context, roleList []string) ([]*rbacmodel.Permission, error) { + var allPerms = make([]*rbacmodel.Permission, 0) for _, name := range roleList { - r, err := datasource.GetRoleManager().GetRole(ctx, name) + r, err := rbac.Instance().GetRole(ctx, name) if err == nil { allPerms = append(allPerms, r.Perms...) continue } - if err == datasource.ErrRoleNotExist { + if err == rbac.ErrRoleNotExist { log.Warn(fmt.Sprintf("role [%s] not exist", name)) continue } @@ -105,7 +105,7 @@ func getPermsByRoles(ctx context.Context, roleList []string) ([]*rbac.Permission // 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) { +func GetLabel(perms []*rbacmodel.Permission, targetResource, verb string) (allow bool, labelList []map[string]string) { for _, perm := range perms { a, l := GetLabelFromSinglePerm(perm, targetResource, verb) if !a { @@ -123,7 +123,7 @@ func GetLabel(perms []*rbac.Permission, targetResource, verb string) (allow bool // 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) { +func GetLabelFromSinglePerm(perm *rbacmodel.Permission, targetResource, verb string) (allow bool, labelList []map[string]string) { if !allowVerb(perm.Verbs, verb) { return false, nil } @@ -140,7 +140,7 @@ func allowVerb(haystack []string, needle string) bool { return false } -func getResourceLabel(resources []*rbac.Resource, needle string) (allow bool, labelList []map[string]string) { +func getResourceLabel(resources []*rbacmodel.Resource, needle string) (allow bool, labelList []map[string]string) { for _, resource := range resources { // filter the same resource if resource.Type != needle { diff --git a/server/service/rbac/perm_service.go b/server/service/rbac/perm_service.go index 961e43054..351b8eb33 100644 --- a/server/service/rbac/perm_service.go +++ b/server/service/rbac/perm_service.go @@ -20,24 +20,24 @@ package rbac import ( "context" - "github.com/apache/servicecomb-service-center/datasource" + "github.com/apache/servicecomb-service-center/datasource/rbac" errorsEx "github.com/apache/servicecomb-service-center/pkg/errors" - "github.com/go-chassis/cari/rbac" + rbacmodel "github.com/go-chassis/cari/rbac" ) // ListSelfPerms list the user permission from ctx -func ListSelfPerms(ctx context.Context) ([]*rbac.Permission, error) { +func ListSelfPerms(ctx context.Context) ([]*rbacmodel.Permission, error) { user := UserFromContext(ctx) if len(user) == 0 { - return nil, rbac.NewError(rbac.ErrUnauthorized, errorsEx.MsgListSelfPermsFailed) + return nil, rbacmodel.NewError(rbacmodel.ErrUnauthorized, errorsEx.MsgListSelfPermsFailed) } - account, err := datasource.GetAccountManager().GetAccount(ctx, user) + account, err := rbac.Instance().GetAccount(ctx, user) if err != nil { return nil, err } - var perms []*rbac.Permission + var perms []*rbacmodel.Permission for _, roleName := range account.Roles { - role, err := datasource.GetRoleManager().GetRole(ctx, roleName) + role, err := rbac.Instance().GetRole(ctx, roleName) if err != nil { return nil, err } diff --git a/server/service/rbac/role_service.go b/server/service/rbac/role_service.go index a3175965a..9bb24eeda 100644 --- a/server/service/rbac/role_service.go +++ b/server/service/rbac/role_service.go @@ -22,17 +22,16 @@ import ( "errors" "fmt" - quotasvc "github.com/apache/servicecomb-service-center/server/service/quota" - "github.com/go-chassis/cari/discovery" - "github.com/go-chassis/cari/rbac" - - "github.com/apache/servicecomb-service-center/datasource" + "github.com/apache/servicecomb-service-center/datasource/rbac" errorsEx "github.com/apache/servicecomb-service-center/pkg/errors" "github.com/apache/servicecomb-service-center/pkg/log" + quotasvc "github.com/apache/servicecomb-service-center/server/service/quota" "github.com/apache/servicecomb-service-center/server/service/validator" + "github.com/go-chassis/cari/discovery" + rbacmodel "github.com/go-chassis/cari/rbac" ) -func CreateRole(ctx context.Context, r *rbac.Role) error { +func CreateRole(ctx context.Context, r *rbacmodel.Role) error { err := validator.ValidateCreateRole(r) if err != nil { log.Error(fmt.Sprintf("create role [%s] failed", r.Name), err) @@ -40,39 +39,39 @@ func CreateRole(ctx context.Context, r *rbac.Role) error { } quotaErr := quotasvc.ApplyRole(ctx, 1) if quotaErr != nil { - return rbac.NewError(rbac.ErrRoleNoQuota, quotaErr.Error()) + return rbacmodel.NewError(rbacmodel.ErrRoleNoQuota, quotaErr.Error()) } - err = datasource.GetRoleManager().CreateRole(ctx, r) + err = rbac.Instance().CreateRole(ctx, r) if err == nil { log.Info(fmt.Sprintf("create role [%s] success", r.Name)) return nil } log.Error(fmt.Sprintf("create role [%s] failed", r.Name), err) - if err == datasource.ErrRoleDuplicated { - return rbac.NewError(rbac.ErrRoleConflict, err.Error()) + if err == rbac.ErrRoleDuplicated { + return rbacmodel.NewError(rbacmodel.ErrRoleConflict, err.Error()) } return err } -func GetRole(ctx context.Context, name string) (*rbac.Role, error) { - resp, err := datasource.GetRoleManager().GetRole(ctx, name) +func GetRole(ctx context.Context, name string) (*rbacmodel.Role, error) { + resp, err := rbac.Instance().GetRole(ctx, name) if err == nil { return resp, nil } - if err == datasource.ErrRoleNotExist { - return nil, rbac.NewError(rbac.ErrRoleNotExist, "") + if err == rbac.ErrRoleNotExist { + return nil, rbacmodel.NewError(rbacmodel.ErrRoleNotExist, "") } return nil, err } -func ListRole(ctx context.Context) ([]*rbac.Role, int64, error) { - return datasource.GetRoleManager().ListRole(ctx) +func ListRole(ctx context.Context) ([]*rbacmodel.Role, int64, error) { + return rbac.Instance().ListRole(ctx) } func RoleExist(ctx context.Context, name string) (bool, error) { - return datasource.GetRoleManager().RoleExist(ctx, name) + return rbac.Instance().RoleExist(ctx, name) } func DeleteRole(ctx context.Context, name string) error { @@ -86,12 +85,12 @@ func DeleteRole(ctx context.Context, name string) error { } if !exist { log.Error(fmt.Sprintf("role [%s] not exist", name), err) - return rbac.NewError(rbac.ErrRoleNotExist, "") + return rbacmodel.NewError(rbacmodel.ErrRoleNotExist, "") } - succeed, err := datasource.GetRoleManager().DeleteRole(ctx, name) + succeed, err := rbac.Instance().DeleteRole(ctx, name) if err != nil { - if errors.Is(err, datasource.ErrRoleBindingExist) { - return rbac.NewError(rbac.ErrRoleIsBound, "") + if errors.Is(err, rbac.ErrRoleBindingExist) { + return rbacmodel.NewError(rbacmodel.ErrRoleIsBound, "") } return err } @@ -101,7 +100,7 @@ func DeleteRole(ctx context.Context, name string) error { return nil } -func EditRole(ctx context.Context, name string, a *rbac.Role) error { +func EditRole(ctx context.Context, name string, a *rbacmodel.Role) error { if err := illegalRoleCheck(name); err != nil { return err } @@ -112,7 +111,7 @@ func EditRole(ctx context.Context, name string, a *rbac.Role) error { } if !exist { log.Error(fmt.Sprintf("role [%s] not exist", name), err) - return rbac.NewError(rbac.ErrRoleNotExist, "") + return rbacmodel.NewError(rbacmodel.ErrRoleNotExist, "") } oldRole, err := GetRole(ctx, name) if err != nil { @@ -122,7 +121,7 @@ func EditRole(ctx context.Context, name string, a *rbac.Role) error { oldRole.Perms = a.Perms - err = datasource.GetRoleManager().UpdateRole(ctx, name, oldRole) + err = rbac.Instance().UpdateRole(ctx, name, oldRole) if err != nil { log.Error("can not edit role info", err) return err @@ -132,8 +131,8 @@ func EditRole(ctx context.Context, name string, a *rbac.Role) error { } func illegalRoleCheck(role string) error { - if role == rbac.RoleAdmin || role == rbac.RoleDeveloper { - return rbac.NewError(rbac.ErrForbidOperateBuildInRole, errorsEx.MsgCantOperateBuildInRole) + if role == rbacmodel.RoleAdmin || role == rbacmodel.RoleDeveloper { + return rbacmodel.NewError(rbacmodel.ErrForbidOperateBuildInRole, errorsEx.MsgCantOperateBuildInRole) } return nil }