Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 账号管理-可见范围鉴权调整 --story=121498225 #1240

Open
wants to merge 1 commit into
base: v1.7.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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