| /* |
| * 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 is dao layer API to help service center manage account, policy and role info |
| package rbac |
| |
| import ( |
| "context" |
| "fmt" |
| "strconv" |
| "time" |
| |
| "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/privacy" |
| "github.com/apache/servicecomb-service-center/pkg/util" |
| 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/dlock" |
| rbacmodel "github.com/go-chassis/cari/rbac" |
| ) |
| |
| // CreateAccount save account info |
| func CreateAccount(ctx context.Context, a *rbacmodel.Account) error { |
| quotaErr := quotasvc.ApplyAccount(ctx, 1) |
| if quotaErr != nil { |
| return rbacmodel.NewError(rbacmodel.ErrAccountNoQuota, quotaErr.Error()) |
| } |
| err := validator.ValidateCreateAccount(a) |
| if err != nil { |
| log.Error(fmt.Sprintf("create account [%s] failed", a.Name), err) |
| return discovery.NewError(discovery.ErrInvalidParams, err.Error()) |
| } |
| if len(a.Status) == 0 { |
| a.Status = "active" |
| } |
| err = a.Check() |
| if err != nil { |
| log.Error(fmt.Sprintf("create account [%s] failed", a.Name), err) |
| return discovery.NewError(discovery.ErrInvalidParams, err.Error()) |
| } |
| if err = checkRoleNames(ctx, a.Roles); err != nil { |
| return rbacmodel.NewError(rbacmodel.ErrAccountHasInvalidRole, err.Error()) |
| } |
| |
| lockKey := "/account-creating/" + a.Name |
| if err := dlock.TryLock(lockKey, -1); err != nil { |
| err = fmt.Errorf("account %s is creating, err: %s", a.Name, err.Error()) |
| return discovery.NewError(discovery.ErrInvalidParams, err.Error()) |
| } |
| defer func() { |
| if err := dlock.Unlock(lockKey); err != nil { |
| log.Error("unlock failed", err) |
| } |
| }() |
| |
| a.Password, err = privacy.ScryptPassword(a.Password) |
| if err != nil { |
| msg := fmt.Sprintf("failed to hash account pwd, account name %s", a.Name) |
| log.Error(msg, err) |
| return err |
| } |
| a.Role = "" |
| a.CurrentPassword = "" |
| a.ID = util.GenerateUUID() |
| a.CreateTime = strconv.FormatInt(time.Now().Unix(), 10) |
| a.UpdateTime = a.CreateTime |
| |
| 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 == 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 *rbacmodel.Account) error { |
| // todo params validation |
| err := validator.ValidateUpdateAccount(a) |
| if err != nil { |
| return discovery.NewError(discovery.ErrInvalidParams, err.Error()) |
| } |
| if err = illegalAccountCheck(ctx, name); err != nil { |
| return err |
| } |
| if len(a.Status) == 0 && len(a.Roles) == 0 { |
| return discovery.NewError(discovery.ErrInvalidParams, "status and roles cannot be empty both") |
| } |
| |
| oldAccount, err := GetAccount(ctx, name) |
| if err != nil { |
| log.Error(fmt.Sprintf("get account [%s] failed", name), err) |
| return err |
| } |
| if len(a.Status) != 0 { |
| oldAccount.Status = a.Status |
| } |
| if len(a.Roles) != 0 { |
| oldAccount.Roles = a.Roles |
| } |
| if err = checkRoleNames(ctx, oldAccount.Roles); err != nil { |
| return rbacmodel.NewError(rbacmodel.ErrAccountHasInvalidRole, err.Error()) |
| } |
| err = rbac.Instance().UpdateAccount(ctx, name, oldAccount) |
| if err != nil { |
| log.Error("can not edit account info", err) |
| return err |
| } |
| log.Info(fmt.Sprintf("account [%s] is edit", oldAccount.ID)) |
| return nil |
| } |
| |
| func GetAccount(ctx context.Context, name string) (*rbacmodel.Account, error) { |
| r, err := rbac.Instance().GetAccount(ctx, name) |
| if err != nil { |
| if err == rbac.ErrAccountNotExist { |
| msg := fmt.Sprintf("account [%s] not exist", name) |
| return nil, rbacmodel.NewError(rbacmodel.ErrAccountNotExist, msg) |
| } |
| return nil, err |
| } |
| return r, nil |
| } |
| func ListAccount(ctx context.Context) ([]*rbacmodel.Account, int64, error) { |
| return rbac.Instance().ListAccount(ctx) |
| } |
| func AccountExist(ctx context.Context, name string) (bool, error) { |
| 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 := 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 rbacmodel.NewError(rbacmodel.ErrAccountNotExist, msg) |
| } |
| _, err = rbac.Instance().DeleteAccount(ctx, []string{name}) |
| return err |
| } |
| |
| // EditAccount save account info |
| 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 rbacmodel.NewError(rbacmodel.ErrAccountNotExist, "") |
| } |
| |
| err = rbac.Instance().UpdateAccount(ctx, a.Name, a) |
| if err != nil { |
| log.Error("can not edit account info", err) |
| return err |
| } |
| log.Info(fmt.Sprintf("account [%s] is edit", a.ID)) |
| return nil |
| } |
| |
| func checkRoleNames(ctx context.Context, roles []string) error { |
| for _, name := range roles { |
| exist, err := RoleExist(ctx, name) |
| if err != nil { |
| log.Error(fmt.Sprintf("check role [%s] exist failed", name), err) |
| return err |
| } |
| if !exist { |
| return rbac.ErrRoleNotExist |
| } |
| } |
| return nil |
| } |
| |
| func illegalAccountCheck(ctx context.Context, target string) error { |
| if target == RootName { |
| return rbacmodel.NewError(rbacmodel.ErrForbidOperateBuildInAccount, errorsEx.MsgCantOperateRoot) |
| } |
| changer := UserFromContext(ctx) |
| if target == changer { |
| return rbacmodel.NewError(rbacmodel.ErrForbidOperateSelfAccount, errorsEx.MsgCantOperateYour) |
| } |
| return nil |
| } |
| |
| func AccountUsage(ctx context.Context) (int64, error) { |
| _, used, err := rbac.Instance().ListAccount(ctx) |
| if err != nil { |
| return 0, err |
| } |
| return used, nil |
| } |