fix: filter organization nodes by policy tags (#19507)

Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
Jian Qiu
2024-02-20 09:53:19 +08:00
committed by GitHub
co-authored by Qiu Jian
parent 8321100c51
commit ca7621cfb0
5 changed files with 257 additions and 3 deletions
+7
View File
@@ -180,6 +180,13 @@ type OrganizationNodeListInput struct {
OrgType TOrgType `json:"org_type"`
Level int `json:"level"`
// domain tags filter imposed by policy
PolicyDomainTags tagutils.TTagSetList `json:"policy_domain_tags"`
// project tags filter imposed by policy
PolicyProjectTags tagutils.TTagSetList `json:"policy_project_tags"`
// object tags filter imposed by policy
PolicyObjectTags tagutils.TTagSetList `json:"policy_object_tags"`
}
type SProjectOrganization struct {
+43 -1
View File
@@ -20,6 +20,7 @@ import (
"database/sql"
"encoding/binary"
"fmt"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
@@ -277,6 +278,27 @@ func (orgNode *SOrganizationNode) ValidateUpdateData(
return input, nil
}
func tagSetList2Conditions(tagsetList tagutils.TTagSetList, keys []string, q *sqlchemy.SQuery) sqlchemy.ICondition {
for i := range keys {
if !strings.HasPrefix(keys[i], "org:") {
keys[i] = "org:" + keys[i]
}
}
conds := make([]sqlchemy.ICondition, 0)
paths := tagutils.TagSetList2Paths(tagsetList, keys)
for i := range paths {
label := api.JoinLabels(paths[i]...)
labelSlash := label + api.OrganizationLabelSeparator
conds = append(conds, sqlchemy.Contains(q.Field("full_label"), labelSlash))
conds = append(conds, sqlchemy.Startswith(q.Field("full_label"), labelSlash))
conds = append(conds, sqlchemy.Equals(q.Field("full_label"), label))
}
if len(conds) > 0 {
return sqlchemy.OR(conds...)
}
return nil
}
// 项目列表
func (manager *SOrganizationNodeManager) ListItemFilter(
ctx context.Context,
@@ -300,7 +322,27 @@ func (manager *SOrganizationNodeManager) ListItemFilter(
return nil, errors.Wrapf(err, "FetchByIdOrName %s", query.OrgId)
}
}
q = q.Equals("org_id", orgObj.GetId())
org := orgObj.(*SOrganization)
q = q.Equals("org_id", org.GetId())
var cond sqlchemy.ICondition
switch org.Type {
case api.OrgTypeDomain:
if !query.PolicyDomainTags.IsEmpty() {
cond = tagSetList2Conditions(query.PolicyDomainTags, org.GetKeys(), q)
}
case api.OrgTypeProject:
if !query.PolicyProjectTags.IsEmpty() {
cond = tagSetList2Conditions(query.PolicyProjectTags, org.GetKeys(), q)
}
case api.OrgTypeObject:
if !query.PolicyObjectTags.IsEmpty() {
cond = tagSetList2Conditions(query.PolicyObjectTags, org.GetKeys(), q)
}
}
if cond != nil {
q = q.Filter(cond)
}
}
if len(query.OrgType) > 0 {
-2
View File
@@ -249,8 +249,6 @@ func (manager *SProjectManager) ListItemFilter(
userCred mcclient.TokenCredential,
query api.ProjectListInput,
) (*sqlchemy.SQuery, error) {
log.Debugf("ProjectManager ListItemFilter query %s", jsonutils.Marshal(query).String())
var err error
q, err = manager.SIdentityBaseResourceManager.ListItemFilter(ctx, q, userCred, query.IdentityBaseResourceListInput)
+33
View File
@@ -247,3 +247,36 @@ func TagsetMap2MapString(oTags map[string]TTagSet) map[string]string {
}
return ret
}
func TagSet2Paths(tagSet TTagSet, keys []string) [][]string {
ret := make([][]string, 0)
tagMap := tagset2Map(tagSet)
for _, k := range keys {
if vs, ok := tagMap[k]; ok {
nret := make([][]string, 0)
for _, v := range vs {
if len(ret) > 0 {
for i := range ret {
cret := make([]string, len(ret[i]), len(ret[i])+1)
copy(cret, ret[i])
cret = append(cret, v)
nret = append(nret, cret)
}
} else {
nret = append(nret, []string{v})
}
}
ret = nret
}
}
return ret
}
func TagSetList2Paths(tagsList TTagSetList, keys []string) [][]string {
ret := make([][]string, 0)
for i := range tagsList {
paths := TagSet2Paths(tagsList[i], keys)
ret = append(ret, paths...)
}
return ret
}
+174
View File
@@ -633,3 +633,177 @@ func TestTagSetKeyPrefix(t *testing.T) {
}
}
}
func TestTagSet2Paths(t *testing.T) {
cases := []struct {
tagset TTagSet
keys []string
paths [][]string
}{
{
tagset: TTagSet{
{
Key: "部门",
Value: "技术",
},
{
Key: "环境",
Value: "测试",
},
},
keys: []string{
"部门", "环境",
},
paths: [][]string{
{
"技术", "测试",
},
},
},
{
tagset: TTagSet{
{
Key: "部门",
Value: "技术",
},
{
Key: "环境",
Value: "测试",
},
{
Key: "环境",
Value: "研发",
},
},
keys: []string{
"部门", "环境",
},
paths: [][]string{
{
"技术", "测试",
},
{
"技术", "研发",
},
},
},
{
tagset: TTagSet{
{
Key: "部门",
Value: "技术",
},
{
Key: "环境",
Value: "测试",
},
{
Key: "业务",
Value: "TDCC",
},
},
keys: []string{
"业务", "部门", "环境",
},
paths: [][]string{
{
"TDCC", "技术", "测试",
},
},
},
{
tagset: TTagSet{
{
Key: "部门",
Value: "技术",
},
{
Key: "环境",
Value: "测试",
},
{
Key: "业务",
Value: "TDCC",
},
{
Key: "业务",
Value: "TKE",
},
},
keys: []string{
"业务", "部门", "环境",
},
paths: [][]string{
{
"TDCC", "技术", "测试",
},
{
"TKE", "技术", "测试",
},
},
},
{
tagset: TTagSet{
{
Key: "部门",
Value: "技术",
},
{
Key: "环境",
Value: "测试",
},
{
Key: "环境",
Value: "生产",
},
{
Key: "业务",
Value: "TDCC",
},
{
Key: "业务",
Value: "TKE",
},
},
keys: []string{
"业务", "部门", "环境",
},
paths: [][]string{
{
"TDCC", "技术", "测试",
},
{
"TKE", "技术", "测试",
},
{
"TDCC", "技术", "生产",
},
{
"TKE", "技术", "生产",
},
},
},
{
tagset: TTagSet{
{
Key: "org:系统",
Value: "G-BOOK",
},
},
keys: []string{
"org:系统", "org:业务模块", "org:環境",
},
paths: [][]string{
{
"G-BOOK",
},
},
},
}
for _, c := range cases {
paths := TagSet2Paths(c.tagset, c.keys)
if jsonutils.Marshal(paths).String() != jsonutils.Marshal(c.paths).String() {
t.Errorf("tagset: %s keys: %s want %s got %s", jsonutils.Marshal(c.tagset), jsonutils.Marshal(c.keys), jsonutils.Marshal(c.paths), jsonutils.Marshal(paths))
}
}
}