datasource/etcd/account.go (201 lines of code) (raw):

/* * 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 etcd import ( "context" "encoding/json" "strconv" "time" crbac "github.com/go-chassis/cari/rbac" "github.com/go-chassis/foundation/stringutil" "github.com/little-cui/etcdadpt" "github.com/apache/servicecomb-service-center/datasource" "github.com/apache/servicecomb-service-center/datasource/etcd/path" esync "github.com/apache/servicecomb-service-center/datasource/etcd/sync" "github.com/apache/servicecomb-service-center/datasource/rbac" "github.com/apache/servicecomb-service-center/pkg/log" ) func init() { rbac.Install("etcd", NewRbacDAO) rbac.Install("embeded_etcd", NewRbacDAO) rbac.Install("embedded_etcd", NewRbacDAO) } func NewRbacDAO(_ rbac.Options) (rbac.DAO, error) { return &RbacDAO{}, nil } type RbacDAO struct { } func (ds *RbacDAO) CreateAccount(ctx context.Context, a *crbac.Account) error { exist, err := ds.AccountExist(ctx, a.Name) if err != nil { log.Error("can not save account info", err) return err } if exist { return rbac.ErrAccountDuplicated } opts, err := GenAccountOpts(a, etcdadpt.ActionPut) if err != nil { log.Error("", err) return err } syncOpts, err := esync.GenCreateOpts(ctx, datasource.ResourceAccount, a) if err != nil { log.Error("fail to create sync opts", err) return err } opts = append(opts, syncOpts...) err = etcdadpt.Txn(ctx, opts) if err != nil { log.Error("can not save account info", err) return err } log.Info("create new account: " + a.ID) return nil } func GenAccountOpts(a *crbac.Account, action etcdadpt.Action) ([]etcdadpt.OpOptions, error) { opts := make([]etcdadpt.OpOptions, 0) value, err := json.Marshal(a) if err != nil { log.Error("account info is invalid", err) return nil, err } opts = append(opts, etcdadpt.OpOptions{ Key: stringutil.Str2bytes(path.GenerateAccountKey(a.Name)), Value: value, Action: action, }) for _, r := range a.Roles { opt := etcdadpt.OpOptions{ Key: stringutil.Str2bytes(path.GenRoleAccountIdxKey(r, a.Name)), Action: action, } opts = append(opts, opt) } return opts, nil } func (ds *RbacDAO) AccountExist(ctx context.Context, name string) (bool, error) { return etcdadpt.Exist(ctx, path.GenerateRBACAccountKey(name)) } func (ds *RbacDAO) GetAccount(ctx context.Context, name string) (*crbac.Account, error) { kv, err := etcdadpt.Get(ctx, path.GenerateRBACAccountKey(name)) if err != nil { return nil, err } if kv == nil { return nil, rbac.ErrAccountNotExist } account := &crbac.Account{} err = json.Unmarshal(kv.Value, account) if err != nil { log.Error("account info format invalid", err) return nil, err } ds.compatibleOldVersionAccount(account) return account, nil } func (ds *RbacDAO) compatibleOldVersionAccount(a *crbac.Account) { // old version use Role, now use Roles // Role/Roles will not exist at the same time if len(a.Role) == 0 { return } a.Roles = []string{a.Role} a.Role = "" } func (ds *RbacDAO) ListAccount(ctx context.Context) ([]*crbac.Account, int64, error) { kvs, n, err := etcdadpt.List(ctx, path.GenerateRBACAccountKey("")) if err != nil { return nil, 0, err } accounts := make([]*crbac.Account, 0, n) for _, v := range kvs { a := &crbac.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 = "" ds.compatibleOldVersionAccount(a) accounts = append(accounts, a) } return accounts, n, nil } func (ds *RbacDAO) DeleteAccount(ctx context.Context, names []string) (bool, error) { if len(names) == 0 { return false, nil } var allOpts []etcdadpt.OpOptions for _, name := range names { a, err := ds.GetAccount(ctx, name) if err != nil { log.Error("", err) continue //do not fail if some account is invalid } if a == nil { log.Warn("can not find account") continue } opts, err := GenAccountOpts(a, etcdadpt.ActionDelete) if err != nil { log.Error("", err) continue //do not fail if some account is invalid } allOpts = append(allOpts, opts...) syncOpts, err := esync.GenDeleteOpts(ctx, datasource.ResourceAccount, a.Name, a) if err != nil { log.Error("fail to create sync opts", err) return false, err } allOpts = append(allOpts, syncOpts...) } err := etcdadpt.Txn(ctx, allOpts) if err != nil { log.Error(rbac.ErrDeleteAccountFailed.Error(), err) return false, err } return true, nil } func (ds *RbacDAO) UpdateAccount(ctx context.Context, _ string, account *crbac.Account) error { var ( opts []etcdadpt.OpOptions err error ) account.UpdateTime = strconv.FormatInt(time.Now().Unix(), 10) opts, err = GenAccountOpts(account, etcdadpt.ActionPut) if err != nil { log.Error("GenAccountOpts failed", err) return err } old, err := ds.GetAccount(ctx, account.Name) if err != nil { log.Error("GetAccount failed", err) return err } for _, r := range old.Roles { if hasRole(account, r) { continue } opt := etcdadpt.OpOptions{ Key: stringutil.Str2bytes(path.GenRoleAccountIdxKey(r, old.Name)), Action: etcdadpt.ActionDelete, } opts = append(opts, opt) } syncOpts, err := esync.GenUpdateOpts(ctx, datasource.ResourceAccount, account) if err != nil { log.Error("fail to create sync opts", err) return err } opts = append(opts, syncOpts...) err = etcdadpt.Txn(ctx, opts) if err != nil { log.Error("BatchCommit failed", err) } return err } func hasRole(account *crbac.Account, r string) bool { for _, n := range account.Roles { if r == n { return true } } return false }