Merge pull request #13371 from rainzm/secret/resource

Classification of resources
This commit is contained in:
Zexi Li
2022-02-16 17:51:51 +08:00
committed by GitHub
28 changed files with 628 additions and 8 deletions
+10 -8
View File
@@ -45,6 +45,7 @@ const (
CLOUD_TAG_PREFIX = dbapi.CLOUD_TAG_PREFIX
USER_TAG_PREFIX = dbapi.USER_TAG_PREFIX
SYS_CLOUD_TAG_PREFIX = dbapi.SYS_CLOUD_TAG_PREFIX
CLASS_TAG_PREFIX = dbapi.CLASS_TAT_PREFIX
// TAG_DELETE_RANGE_USER = "user"
// TAG_DELETE_RANGE_CLOUD = CLOUD_TAG_PREFIX // "cloud"
@@ -602,14 +603,15 @@ func (manager *SMetadataManager) rawSetValues(ctx context.Context, objType strin
if replace {
records := []SMetadata{}
q := manager.Query().Equals("id", idStr).NotLike("key", `\_\_%`) //避免删除系统内置的metadata, _ 在mysql里面有特殊含义,需要转义
switch replaceRange {
case USER_TAG_PREFIX:
q = q.Startswith("key", USER_TAG_PREFIX)
case CLOUD_TAG_PREFIX:
q = q.Startswith("key", CLOUD_TAG_PREFIX)
case SYS_CLOUD_TAG_PREFIX:
q = q.Startswith("key", SYS_CLOUD_TAG_PREFIX)
}
// switch replaceRange {
// case USER_TAG_PREFIX:
// q = q.Startswith("key", USER_TAG_PREFIX)
// case CLOUD_TAG_PREFIX:
// q = q.Startswith("key", CLOUD_TAG_PREFIX)
// case SYS_CLOUD_TAG_PREFIX:
// q = q.Startswith("key", SYS_CLOUD_TAG_PREFIX)
// }
q = q.Startswith("key", replaceRange)
q = q.Filter(sqlchemy.NOT(sqlchemy.In(q.Field("key"), keys)))
if err := FetchModelObjects(manager, q, &records); err != nil {
log.Errorf("failed to fetch metadata error: %v", err)
+113
View File
@@ -29,6 +29,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/util/rbacutils"
"yunion.io/x/onecloud/pkg/util/stringutils2"
"yunion.io/x/onecloud/pkg/util/tagutils"
@@ -274,6 +275,70 @@ func (model *SStandaloneAnonResourceBase) SetCloudMetadataAll(ctx context.Contex
return Metadata.SetAll(ctx, model, userTags, userCred, USER_TAG_PREFIX)
}
func (model *SStandaloneAnonResourceBase) SetClassMetadataValues(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error {
err := Metadata.SetValuesWithLog(ctx, model, dictstore, userCred)
if err != nil {
return errors.Wrap(err, "SetValuesWithLog")
}
return nil
}
func (model *SStandaloneAnonResourceBase) SetClassMetadataAll(ctx context.Context, dictstore map[string]string, userCred mcclient.TokenCredential) error {
afterCheck := make(map[string]interface{}, len(dictstore))
for k, v := range dictstore {
if !strings.HasPrefix(k, CLASS_TAG_PREFIX) {
afterCheck[CLASS_TAG_PREFIX+k] = v
} else {
afterCheck[k] = v
}
}
err := Metadata.SetAll(ctx, model, afterCheck, userCred, CLASS_TAG_PREFIX)
if err != nil {
return errors.Wrap(err, "SetAll")
}
return nil
}
func (model *SStandaloneAnonResourceBase) Inherit(ctx context.Context, sonModel *SStandaloneAnonResourceBase) error {
metadata, err := model.GetAllClassMetadata()
if err != nil {
return errors.Wrap(err, "GetAllPureMetadata")
}
if len(metadata) == 0 {
return nil
}
userCred := auth.AdminCredential()
return sonModel.SetClassMetadataAll(ctx, metadata, userCred)
}
type IClassMetadataOwner interface {
GetAllClassMetadata() (map[string]string, error)
}
func IsInSameClass(ctx context.Context, cmo1, cmo2 IClassMetadataOwner) (bool, error) {
pureTags, err := cmo1.GetAllClassMetadata()
if err != nil {
return false, errors.Wrap(err, "GetAllPureMetadata")
}
pureTagsP, err := cmo2.GetAllClassMetadata()
if err != nil {
return false, errors.Wrap(err, "GetAllPureMetadata")
}
if len(pureTags) != len(pureTagsP) {
return false, nil
}
for k, v := range pureTags {
if vp, ok := pureTagsP[k]; !ok || vp != v {
return false, nil
}
}
return true, nil
}
func (model *SStandaloneAnonResourceBase) IsInSameClass(ctx context.Context, pModel *SStandaloneAnonResourceBase) (bool, error) {
return IsInSameClass(ctx, model, pModel)
}
func (model *SStandaloneAnonResourceBase) SetSysCloudMetadataAll(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error {
err := Metadata.SetAll(ctx, model, dictstore, userCred, SYS_CLOUD_TAG_PREFIX)
if err != nil {
@@ -326,6 +391,18 @@ func (model *SStandaloneAnonResourceBase) GetAllCloudMetadata() (map[string]stri
return ret, nil
}
func (model *SStandaloneAnonResourceBase) GetAllClassMetadata() (map[string]string, error) {
meta, err := Metadata.GetAll(nil, model, nil, CLASS_TAG_PREFIX, nil)
if err != nil {
return nil, errors.Wrap(err, "Metadata.GetAll")
}
ret := make(map[string]string)
for k, v := range meta {
ret[k[len(CLASS_TAG_PREFIX):]] = v
}
return ret, nil
}
// 获取资源标签(元数据)
func (model *SStandaloneAnonResourceBase) GetDetailsMetadata(ctx context.Context, userCred mcclient.TokenCredential, input apis.GetMetadataInput) (apis.GetMetadataOutput, error) {
val, err := Metadata.GetAll(ctx, model, input.Field, input.Prefix, userCred)
@@ -393,6 +470,42 @@ func (model *SStandaloneAnonResourceBase) PerformSetUserMetadata(ctx context.Con
return nil, nil
}
// 更新资源的 class 标签
func (model *SStandaloneAnonResourceBase) PerformClassMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformClassMetadataInput) (jsonutils.JSONObject, error) {
dictStore := make(map[string]interface{})
for k, v := range input {
dictStore[CLASS_TAG_PREFIX+k] = v
}
err := model.SetUserMetadataValues(ctx, dictStore, userCred)
if err != nil {
return nil, errors.Wrap(err, "SetUserMetadataValues")
}
return nil, nil
}
// 全量替换资源的所有 class 标签
func (model *SStandaloneAnonResourceBase) PerformSetClassMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformSetClassMetadataInput) (jsonutils.JSONObject, error) {
dictStore := make(map[string]string)
for k, v := range input {
if len(k) > 64-len(CLASS_TAG_PREFIX) {
return nil, httperrors.NewInputParameterError("input key too long > %d", 64-len(CLASS_TAG_PREFIX))
}
if len(v) > 65535 {
return nil, httperrors.NewInputParameterError("input value too long > %d", 65535)
}
dictStore[k] = v
}
err := model.SetClassMetadataAll(ctx, dictStore, userCred)
if err != nil {
return nil, errors.Wrap(err, "SetUserMetadataAll")
}
return nil, nil
}
func (model *SStandaloneAnonResourceBase) GetDetailsClassMetadata(ctx context.Context, userCred mcclient.TokenCredential, input apis.GetClassMetadataOutput) (apis.GetClassMetadataOutput, error) {
return model.GetAllClassMetadata()
}
type sPolicyTags struct {
PolicyObjectTags tagutils.TTagSetList `json:"policy_object_tags"`
PolicyProjectTags tagutils.TTagSetList `json:"policy_project_tags"`
+24
View File
@@ -19,6 +19,7 @@ import (
"database/sql"
"fmt"
"runtime/debug"
"strings"
"time"
"yunion.io/x/jsonutils"
@@ -520,3 +521,26 @@ func (tenant *STenant) IsDomain() bool {
return false
}
}
func (tenant *STenant) objType() string {
if tenant.IsDomain() {
return "domain"
} else {
return "project"
}
}
func (tenant *STenant) GetAllClassMetadata() (map[string]string, error) {
meta, err := Metadata.rawGetAll(tenant.objType(), tenant.GetId(), nil, CLASS_TAG_PREFIX)
if err != nil {
return nil, errors.Wrap(err, "rawGetAll")
}
ret := make(map[string]string)
for k, v := range meta {
if strings.HasPrefix(k, SYSTEM_ADMIN_PREFIX) {
continue
}
ret[k[len(CLASS_TAG_PREFIX):]] = v
}
return ret, nil
}
+40
View File
@@ -282,6 +282,18 @@ func (model *SVirtualResourceBase) CustomizeCreate(ctx context.Context, userCred
return model.SStatusStandaloneResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
}
func (model *SVirtualResourceBase) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
project, err := model.GetTenantCache(ctx)
if err != nil {
log.Errorf("unable to GetTenantCache: %s", err.Error())
return
}
err = project.Inherit(ctx, &model.SStandaloneAnonResourceBase)
if err != nil {
log.Errorf("unable to inherit class metadata from poject %s: %s", project.GetId(), err.Error())
}
}
func (manager *SVirtualResourceBaseManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
@@ -416,6 +428,27 @@ func (model *SVirtualResourceBase) PerformChangeOwner(ctx context.Context, userC
log.Warningf("tenant_id %s not found", model.ProjectId)
formerObj := NewTenant(model.ProjectId, "unknown", model.DomainId, "unknown")
former = &formerObj
} else {
// check fromer's class metadata
cm, err := former.GetAllClassMetadata()
if err != nil {
return nil, errors.Wrap(err, "unable to GetAllClassMetadata")
}
if len(cm) > 0 {
return nil, httperrors.NewForbiddenError("can't change owner for resource in project with class metadata")
}
}
toer, err := TenantCacheManager.FetchTenantById(ctx, ownerId.GetProjectId())
if err != nil {
return nil, errors.Wrapf(err, "unable to get project %s", ownerId.GetProjectId())
}
toCm, err := toer.GetAllClassMetadata()
if err != nil {
return nil, errors.Wrap(err, "unable to GetAllClassMetadata")
}
if model.Keyword() != "image" && len(toCm) > 0 {
return nil, httperrors.NewForbiddenError("can't change resource's owner as that in project with class metadata")
}
// clean shared projects before update project id
@@ -461,6 +494,13 @@ func (model *SVirtualResourceBase) PerformChangeOwner(ctx context.Context, userC
NewDomainId: ownerId.GetProjectDomainId(),
NewDomain: ownerId.GetProjectDomain(),
}
// set class metadata
err = model.SetClassMetadataAll(ctx, toCm, userCred)
if err != nil {
return nil, errors.Wrap(err, "unable to SetClassMetadataAll")
}
logclient.AddActionLogWithContext(ctx, model, logclient.ACT_CHANGE_OWNER, notes, userCred, true)
return nil, nil
}