Skip to content

Commit

Permalink
feat: 账号管理-可见范围鉴权调整 --story=121498225
Browse files Browse the repository at this point in the history
  • Loading branch information
KooKouse committed Jan 20, 2025
1 parent 4abbcaf commit acadb09
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func (c *CreateLayer4ListenerPreviewExecutor) validateListener(kt *kit.Kit,
return nil
}

rule, err := c.getURLRule(kt, curDetail.CloudClbID, listener.CloudID)
rule, err := c.getURLRule(kt, listener.LbID, listener.ID)
if err != nil {
return err
}
Expand Down Expand Up @@ -223,15 +223,15 @@ func (c *CreateLayer4ListenerPreviewExecutor) validateListener(kt *kit.Kit,
return nil
}

func (c *CreateLayer4ListenerPreviewExecutor) getURLRule(kt *kit.Kit, lbCloudID, listenerCloudID string) (
func (c *CreateLayer4ListenerPreviewExecutor) getURLRule(kt *kit.Kit, lbID, listenerID string) (
*corelb.TCloudLbUrlRule, error) {

switch c.vendor {
case enumor.TCloud:
req := &core.ListReq{
Filter: tools.ExpressionAnd(
tools.RuleEqual("cloud_lb_id", lbCloudID),
tools.RuleEqual("cloud_lbl_id", listenerCloudID),
tools.RuleEqual("lb_id", lbID),
tools.RuleEqual("lbl_id", listenerID),
),
Page: core.NewDefaultBasePage(),
}
Expand Down
11 changes: 8 additions & 3 deletions cmd/cloud-server/service/account/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,18 @@ func (a *accountSvc) GetAccount(cts *rest.Contexts) (interface{}, error) {
func accountDetailFullFill[T protocloud.AccountExtensionGetResp](svc *accountSvc, cts *rest.Contexts,
acc *protocloud.AccountGetResult[T]) (*protocloud.AccountGetResult[T], error) {
acc.RecycleReserveTime = convertRecycleReverseTime(acc.RecycleReserveTime)
status, failedReason, err := svc.getAccountSyncDetail(cts, acc.ID, string(acc.Vendor))
syncDetails, err := svc.getAccountsSyncDetail(cts, acc.ID)
if err != nil {
logs.Errorf("fail to get account sync detail, accountID: %s, rid: %s", acc.ID, cts.Kit.Rid)
return nil, err
}
acc.SyncStatus = status
acc.SyncFailedReason = failedReason
for _, detail := range syncDetails[acc.ID] {
acc.SyncStatus = detail.ResStatus
if detail.ResStatus == string(enumor.SyncFailed) {
acc.SyncFailedReason = string(detail.ResFailedReason)
break
}
}
return acc, nil
}

Expand Down
287 changes: 240 additions & 47 deletions cmd/cloud-server/service/account/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,37 @@
package account

import (
"fmt"

proto "hcm/pkg/api/cloud-server/account"
"hcm/pkg/api/core"
"hcm/pkg/api/core/cloud"
coresync "hcm/pkg/api/core/cloud/sync"
dataproto "hcm/pkg/api/data-service/cloud"
"hcm/pkg/criteria/enumor"
"hcm/pkg/criteria/errf"
"hcm/pkg/dal/dao/tools"
"hcm/pkg/iam/meta"
"hcm/pkg/kit"
"hcm/pkg/logs"
"hcm/pkg/rest"
"hcm/pkg/runtime/filter"
"hcm/pkg/tools/converter"
"hcm/pkg/tools/slice"
)

// ListAccount ...
func (a *accountSvc) ListAccount(cts *rest.Contexts) (interface{}, error) {
return a.list(cts, meta.Account)
return a.listAccount(cts, meta.Account)
}

// ResourceList ...
func (a *accountSvc) ResourceList(cts *rest.Contexts) (interface{}, error) {
return a.list(cts, meta.CloudResource)
return a.listResource(cts, meta.CloudResource)
}

func (a *accountSvc) list(cts *rest.Contexts, typ meta.ResourceType) (interface{}, error) {
req := new(proto.AccountListReq)
func (a *accountSvc) listResource(cts *rest.Contexts, typ meta.ResourceType) (interface{}, error) {
req := new(proto.AccountListResourceReq)
if err := cts.DecodeInto(req); err != nil {
return nil, err
}
Expand Down Expand Up @@ -77,69 +86,253 @@ func (a *accountSvc) list(cts *rest.Contexts, typ meta.ResourceType) (interface{
}
}

accounts, err := a.client.DataService().Global.Account.List(
cts.Kit.Ctx,
cts.Kit.Header(),
&dataproto.AccountListReq{
Filter: reqFilter,
Page: req.Page,
},
)
listReq := &dataproto.AccountListReq{
Filter: reqFilter,
Page: req.Page,
}
accounts, err := a.client.DataService().Global.Account.List(cts.Kit.Ctx, cts.Kit.Header(), listReq)
if err != nil {
logs.Errorf("list account failed, err: %v, rid: %s", err, cts.Kit.Rid)
return nil, err
}

respIDs := make([]string, 0, len(accounts.Details))
for _, one := range accounts.Details {
respIDs = append(respIDs, one.ID)
}
accountDetailsMap, err := a.getAccountsSyncDetail(cts, respIDs...)
if err != nil {
logs.Errorf("get account sync detail failed, err: %v, rid: %s", err, cts.Kit.Rid)
return nil, err
}
for _, one := range accounts.Details {
status, failedReason, err := a.getAccountSyncDetail(cts, one.ID, string(one.Vendor))
for _, detail := range accountDetailsMap[one.ID] {
one.SyncStatus = detail.ResStatus
if detail.ResStatus == string(enumor.SyncFailed) {
one.SyncFailedReason = string(detail.ResFailedReason)
break
}
}
one.RecycleReserveTime = convertRecycleReverseTime(one.RecycleReserveTime)
}

return accounts, nil
}

func (a *accountSvc) listAccount(cts *rest.Contexts, typ meta.ResourceType) (*dataproto.AccountListResult, error) {
req := new(proto.AccountListReq)
if err := cts.DecodeInto(req); err != nil {
return nil, err
}

if err := req.Validate(); err != nil {
return nil, errf.NewFromErr(errf.InvalidParameter, err)
}

// 校验用户是否有查看权限,有权限的ID列表
accountIDs, isAny, err := a.listAuthorized(cts, meta.Find, typ)
if err != nil {
return nil, err
}

if isAny {
accounts, err := a.listAccountByFilter(cts.Kit, req.Filter)
if err != nil {
logs.Errorf("list account by filter failed, err: %v, rid: %s", err, cts.Kit.Rid)
return nil, err
}
err = a.fillAccountSyncDetail(cts, accounts)
if err != nil {
logs.Errorf("fill account sync detail failed, err: %v, rid: %s", err, cts.Kit.Rid)
return nil, err
}
return &dataproto.AccountListResult{
Details: accounts,
Count: uint64(len(accounts)),
}, nil
}

bizAccounts, err := a.listAccountByBiz(cts)
if err != nil {
logs.Errorf("list account by biz failed, err: %v, rid: %s", err, cts.Kit.Rid)
return nil, err
}
accountIDs = append(accountIDs, bizAccounts...)
accountIDs = slice.Unique(accountIDs)

// 构造权限过滤条件
accounts := make([]*cloud.BaseAccount, 0)
for _, ids := range slice.Split(accountIDs, int(core.DefaultMaxPageLimit)) {
innerFilter := tools.ExpressionOr(
tools.RuleJSONContains("managers", cts.Kit.User),
tools.RuleIn("id", ids),
)
// 加上请求里过滤条件
var reqFilter *filter.Expression
if req.Filter != nil && !req.Filter.IsEmpty() {
reqFilter, err = tools.And(innerFilter, req.Filter)
if err != nil {
logs.Errorf("merge filter failed, err: %v, rid: %s", err, cts.Kit.Rid)
return nil, err
}
} else {
reqFilter = innerFilter
}
accountList, err := a.listAccountByFilter(cts.Kit, reqFilter)
if err != nil {
logs.Errorf("list account by filter failed, err: %v, rid: %s", err, cts.Kit.Rid)
return nil, err
}
one.SyncStatus = status
one.SyncFailedReason = failedReason
accounts = append(accounts, accountList...)
}

err = a.fillAccountSyncDetail(cts, accounts)
if err != nil {
logs.Errorf("fill account sync detail failed, err: %v, rid: %s", err, cts.Kit.Rid)
return nil, err
}

return &dataproto.AccountListResult{
Details: accounts,
Count: uint64(len(accounts)),
}, nil
}

// fillAccountSyncDetail 补全同步状态信息
func (a *accountSvc) fillAccountSyncDetail(cts *rest.Contexts, accounts []*cloud.BaseAccount) error {
syncAccountMap := make(map[string]*cloud.BaseAccount)
for _, one := range accounts {
if one.Type == enumor.RegistrationAccount {
continue
}
syncAccountMap[one.ID] = one
}
if len(syncAccountMap) == 0 {
return nil
}
syncDetails, err := a.getAccountsSyncDetail(cts, converter.MapKeyToSlice(syncAccountMap)...)
if err != nil {
logs.Errorf("get account sync detail failed, err: %v, rid: %s", err, cts.Kit.Rid)
return err
}

for _, one := range syncAccountMap {
for _, detail := range syncDetails[one.ID] {
one.SyncStatus = detail.ResStatus
if detail.ResStatus == string(enumor.SyncFailed) {
one.SyncFailedReason = string(detail.ResFailedReason)
break
}
}
one.RecycleReserveTime = convertRecycleReverseTime(one.RecycleReserveTime)
}
return nil
}

func (a *accountSvc) listAccountByFilter(kt *kit.Kit, reqFilter *filter.Expression) ([]*cloud.BaseAccount, error) {
page := &core.BasePage{
Count: false,
Start: 0,
Limit: core.DefaultMaxPageLimit,
Sort: "id",
}
accounts := make([]*cloud.BaseAccount, 0)
for {
listReq := &dataproto.AccountListReq{
Filter: reqFilter,
Page: page,
}
resp, err := a.client.DataService().Global.Account.List(kt.Ctx, kt.Header(), listReq)
if err != nil {
logs.Errorf("list account failed, err: %v, req: %v, rid: %s", err, listReq, kt.Rid)
return nil, err
}
if len(resp.Details) == 0 {
break
}
accounts = append(accounts, resp.Details...)
page.Start += uint32(core.DefaultMaxPageLimit)
}
return accounts, nil
}

func (a *accountSvc) getAccountSyncDetail(cts *rest.Contexts, accountID string,
vendor string) (string, string, error) {
// listAccountByBiz 根据账号可见业务查询账号列表
func (a *accountSvc) listAccountByBiz(cts *rest.Contexts) ([]string, error) {
bizIDs, _, err := a.listAuthorized(cts, meta.Access, meta.Biz)
if err != nil {
return nil, err
}

listReq := &core.ListReq{
Filter: &filter.Expression{
Op: filter.And,
Rules: []filter.RuleFactory{
&filter.AtomRule{
Field: "account_id",
Op: filter.Equal.Factory(),
Value: accountID,
},
&filter.AtomRule{
Field: "vendor",
Op: filter.Equal.Factory(),
Value: vendor,
resultMap := make(map[string]struct{})
for _, ids := range slice.Split(bizIDs, int(core.DefaultMaxPageLimit)) {

intIDs := converter.StringSliceToInt64Slice(ids)
offset := uint32(0)
for {
listReq := &core.ListReq{
Filter: tools.ExpressionAnd(
tools.RuleIn("bk_biz_id", intIDs),
),
Page: &core.BasePage{
Count: false,
Start: offset,
Limit: core.DefaultMaxPageLimit,
},
},
},
Page: &core.BasePage{
Start: 0,
Limit: core.DefaultMaxPageLimit,
},
}
resp, err := a.client.DataService().Global.Account.ListAccountBizRel(cts.Kit.Ctx, cts.Kit.Header(), listReq)
if err != nil {
logs.Errorf("list account biz relation failed, err: %v, req: %v, rid: %s", err, listReq, cts.Kit.Rid)
return nil, err
}
if len(resp.Details) == 0 {
break
}

for _, detail := range resp.Details {
resultMap[detail.AccountID] = struct{}{}
}
offset += uint32(core.DefaultMaxPageLimit)
}
}
accountSyncDetail, err := a.client.DataService().Global.AccountSyncDetail.List(cts.Kit, listReq)
if err != nil {
return "", "", err

return converter.MapKeyToSlice(resultMap), nil
}

func getSliceByPage[T any](data []T, page *core.BasePage) []T {
length := len(data)
if length == 0 {
return []T{}
}
// safe slice
begin := min(int(page.Start), length)
end := min(length, int(page.Start)+int(page.Limit))
return data[begin:end]
}

status := ""
failedReason := ""
for _, one := range accountSyncDetail.Details {
status = one.ResStatus
if one.ResStatus == string(enumor.SyncFailed) {
failedReason = string(one.ResFailedReason)
break
func (a *accountSvc) getAccountsSyncDetail(cts *rest.Contexts, accountIDs ...string) (
map[string][]coresync.AccountSyncDetailTable, error) {

if len(accountIDs) == 0 {
return nil, fmt.Errorf("accountIDs is empty")
}

result := make(map[string][]coresync.AccountSyncDetailTable)
for _, ids := range slice.Split(accountIDs, int(core.DefaultMaxPageLimit)) {
listReq := &core.ListReq{
Filter: tools.ExpressionAnd(
tools.RuleIn("account_id", ids),
),
Page: core.NewDefaultBasePage(),
}
accountSyncDetail, err := a.client.DataService().Global.AccountSyncDetail.List(cts.Kit, listReq)
if err != nil {
logs.Errorf("list account sync detail failed, err: %v, req: %v, rid: %s", err, listReq, cts.Kit.Rid)
return nil, err
}
for _, detail := range accountSyncDetail.Details {
result[detail.AccountID] = append(result[detail.AccountID], detail)
}
}

return status, failedReason, nil
return result, nil
}
2 changes: 1 addition & 1 deletion cmd/cloud-server/service/account/list_with_extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func canListAccountExtension(appCode string) error {

// ListWithExtension 该接口返回了Extension,不包括SecretKey,只提供给安全使用
func (a *accountSvc) ListWithExtension(cts *rest.Contexts) (interface{}, error) {
req := new(proto.AccountListReq)
req := new(proto.AccountListWithExtReq)
if err := cts.DecodeInto(req); err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit acadb09

Please sign in to comment.