diff --git a/cmd/climc/shell/globalvpcs.go b/cmd/climc/shell/globalvpcs.go index 93209c9a33..6b2c214113 100644 --- a/cmd/climc/shell/globalvpcs.go +++ b/cmd/climc/shell/globalvpcs.go @@ -15,6 +15,8 @@ package shell import ( + "fmt" + "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/mcclient" @@ -78,4 +80,30 @@ func init() { printObject(result) return nil }) + + R(&GlobalVpcShowOptions{}, "global-vpc-change-owner-candidate-domains", "Show candiate domains of a global vpc for changing owner", func(s *mcclient.ClientSession, args *GlobalVpcShowOptions) error { + result, err := modules.GlobalVpcs.GetSpecific(s, args.ID, "change-owner-candidate-domains", nil) + if err != nil { + return err + } + printObject(result) + return nil + }) + + type GlobalVpcChangeOwnerOptions struct { + ID string `help:"ID or name of vpc" json:"-"` + ProjectDomain string `json:"project_domain" help:"target domain"` + } + R(&GlobalVpcChangeOwnerOptions{}, "global-vpc-change-owner", "Change owner domain of a global vpc", func(s *mcclient.ClientSession, args *GlobalVpcChangeOwnerOptions) error { + if len(args.ProjectDomain) == 0 { + return fmt.Errorf("empty project_domain") + } + params := jsonutils.Marshal(args) + ret, err := modules.GlobalVpcs.PerformAction(s, args.ID, "change-owner", params) + if err != nil { + return err + } + printObject(ret) + return nil + }) } diff --git a/pkg/cloudcommon/db/domain.go b/pkg/cloudcommon/db/domain.go index 3615880cfa..230b522df6 100644 --- a/pkg/cloudcommon/db/domain.go +++ b/pkg/cloudcommon/db/domain.go @@ -77,6 +77,10 @@ func (model *SDomainizedResourceBase) GetChangeOwnerCandidateDomainIds() []strin return nil } +func (model *SDomainizedResourceBase) GetChangeOwnerRequiredDomainIds() []string { + return nil +} + func ValidateCreateDomainId(domainId string) error { if !consts.GetNonDefaultDomainProjects() && domainId != identity.DEFAULT_DOMAIN_ID { return httperrors.NewForbiddenError("project in non-default domain is prohibited") diff --git a/pkg/cloudcommon/db/domainresource.go b/pkg/cloudcommon/db/domainresource.go index 8826c6a3ee..a094f742e9 100644 --- a/pkg/cloudcommon/db/domainresource.go +++ b/pkg/cloudcommon/db/domainresource.go @@ -24,6 +24,7 @@ import ( "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/apis" + "yunion.io/x/onecloud/pkg/cloudcommon/consts" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/logclient" @@ -133,6 +134,9 @@ func (model *SDomainLevelResourceBase) AllowPerformChangeOwner(ctx context.Conte } func (model *SDomainLevelResourceBase) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeDomainOwnerInput) (jsonutils.JSONObject, error) { + if !consts.GetNonDefaultDomainProjects() { + return nil, errors.Wrap(httperrors.ErrForbidden, "not allow to change owner of domain resource if non_default_domain_projects is turned off") + } if model.GetIStandaloneModel().IsShared() { return nil, errors.Wrap(httperrors.ErrForbidden, "cannot change owner of shared resource") } @@ -162,6 +166,11 @@ func (model *SDomainLevelResourceBase) PerformChangeOwner(ctx context.Context, u if len(candidates) > 0 && !utils.IsInStringArray(ownerId.GetProjectDomainId(), candidates) { return nil, errors.Wrap(httperrors.ErrForbidden, "target domain not in change owner candidate list") } + requires := model.GetIDomainLevelModel().GetChangeOwnerRequiredDomainIds() + log.Debugf("%s required domains: %s", model.Keyword(), requires) + if len(requires) > 0 && !utils.IsInStringArray(ownerId.GetProjectDomainId(), requires) { + return nil, errors.Wrap(httperrors.ErrForbidden, "target domain not in change owner required list") + } if !IsAdminAllowPerform(userCred, model, "change-owner") { return nil, errors.Wrap(httperrors.ErrNotSufficientPrivilege, "require system privileges") diff --git a/pkg/cloudcommon/db/infraresource.go b/pkg/cloudcommon/db/infraresource.go index 84d3a4b1c1..4c6049512b 100644 --- a/pkg/cloudcommon/db/infraresource.go +++ b/pkg/cloudcommon/db/infraresource.go @@ -22,6 +22,7 @@ import ( "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/apis" + "yunion.io/x/onecloud/pkg/cloudcommon/consts" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/rbacutils" @@ -200,6 +201,9 @@ func (model *SInfrasResourceBase) PerformChangeOwner( query jsonutils.JSONObject, input apis.PerformChangeDomainOwnerInput, ) (jsonutils.JSONObject, error) { + if !consts.GetNonDefaultDomainProjects() { + return nil, errors.Wrap(httperrors.ErrForbidden, "not allow to change owner of domain resource if non_default_domain_projects is turned off") + } if model.IsShared() { return nil, errors.Wrap(httperrors.ErrInvalidStatus, "cannot change owner when shared!") } diff --git a/pkg/cloudcommon/db/managed.go b/pkg/cloudcommon/db/managed.go index 6d9ecaefa3..cfbfb7b36d 100644 --- a/pkg/cloudcommon/db/managed.go +++ b/pkg/cloudcommon/db/managed.go @@ -22,6 +22,7 @@ import ( type IOwnerResourceBaseModel interface { GetChangeOwnerCandidateDomainIds() []string + GetChangeOwnerRequiredDomainIds() []string } type IManagedResourceBase interface { diff --git a/pkg/compute/models/globalvpcs.go b/pkg/compute/models/globalvpcs.go index 148d0b142b..25076eeda8 100644 --- a/pkg/compute/models/globalvpcs.go +++ b/pkg/compute/models/globalvpcs.go @@ -240,3 +240,12 @@ func (globalVpc *SGlobalVpc) GetRequiredSharedDomainIds() []string { } return db.ISharableMergeShareRequireDomainIds(requires...) } + +func (globalVpc *SGlobalVpc) GetChangeOwnerRequiredDomainIds() []string { + requires := stringutils2.SSortedStrings{} + vpcs, _ := globalVpc.GetVpcs() + for i := range vpcs { + requires = stringutils2.Append(requires, vpcs[i].DomainId) + } + return requires +} diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index a9355d07c5..58f5cc539d 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -5136,6 +5136,15 @@ func (host *SHost) PerformChangeOwner(ctx context.Context, userCred mcclient.Tok return ret, nil } +func (host *SHost) GetChangeOwnerRequiredDomainIds() []string { + requires := stringutils2.SSortedStrings{} + guests := host.GetGuests() + for i := range guests { + requires = stringutils2.Append(requires, guests[i].DomainId) + } + return requires +} + func GetHostQuotaKeysFromCreateInput(input api.HostCreateInput) quotas.SDomainRegionalCloudResourceKeys { ownerId := &db.SOwnerId{DomainId: input.ProjectDomain} var zone *SZone diff --git a/pkg/compute/models/storages.go b/pkg/compute/models/storages.go index 62bf7dc0ff..1787a382c8 100644 --- a/pkg/compute/models/storages.go +++ b/pkg/compute/models/storages.go @@ -1483,6 +1483,15 @@ func (storage *SStorage) performChangeOwnerInternal(ctx context.Context, userCre return storage.SEnabledStatusInfrasResourceBase.PerformChangeOwner(ctx, userCred, query, input) } +func (storage *SStorage) GetChangeOwnerRequiredDomainIds() []string { + requires := stringutils2.SSortedStrings{} + disks := storage.GetDisks() + for i := range disks { + requires = stringutils2.Append(requires, disks[i].DomainId) + } + return requires +} + func (storage *SStorage) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicDomainInput) (jsonutils.JSONObject, error) { // not allow to perform public for locally connected storage if storage.IsLocal() { diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index 23ac17c7ee..0023cb9f0d 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -1097,6 +1097,15 @@ func (vpc *SVpc) GetChangeOwnerCandidateDomainIds() []string { return db.ISharableMergeChangeOwnerCandidateDomainIds(vpc, candidates...) } +func (vpc *SVpc) GetChangeOwnerRequiredDomainIds() []string { + requires := stringutils2.SSortedStrings{} + wires := vpc.GetWires() + for i := range wires { + requires = stringutils2.Append(requires, wires[i].DomainId) + } + return requires +} + func (vpc *SVpc) GetRequiredSharedDomainIds() []string { wires := vpc.GetWires() if len(wires) == 0 { diff --git a/pkg/compute/models/wires.go b/pkg/compute/models/wires.go index 0a8252feeb..b33d349d78 100644 --- a/pkg/compute/models/wires.go +++ b/pkg/compute/models/wires.go @@ -1024,6 +1024,15 @@ func (wire *SWire) GetChangeOwnerCandidateDomainIds() []string { return db.ISharableMergeChangeOwnerCandidateDomainIds(wire, candidates...) } +func (wire *SWire) GetChangeOwnerRequiredDomainIds() []string { + requires := stringutils2.SSortedStrings{} + networks, _ := wire.getNetworks(nil, rbacutils.ScopeNone) + for i := range networks { + requires = stringutils2.Append(requires, networks[i].DomainId) + } + return requires +} + func (wire *SWire) GetRequiredSharedDomainIds() []string { networks, _ := wire.getNetworks(nil, rbacutils.ScopeNone) if len(networks) == 0 { diff --git a/pkg/mcclient/modules/mod_wires.go b/pkg/mcclient/modules/mod_wires.go index e9ab755306..80603ce9a4 100644 --- a/pkg/mcclient/modules/mod_wires.go +++ b/pkg/mcclient/modules/mod_wires.go @@ -23,7 +23,9 @@ var ( func init() { Wires = NewComputeManager("wire", "wires", []string{"ID", "Name", "Bandwidth", "Zone_ID", - "Zone", "Networks", "VPC", "VPC_ID", "public_scope"}, + "Zone", "Networks", "VPC", "VPC_ID", "public_scope", + "domain_id", + }, []string{}) registerCompute(&Wires)