mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-07 22:24:32 +08:00
229 lines
7.6 KiB
Go
229 lines
7.6 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/util/rbacscope"
|
|
"yunion.io/x/pkg/utils"
|
|
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
|
"yunion.io/x/onecloud/pkg/httperrors"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
|
)
|
|
|
|
const (
|
|
SharedTargetProject = "project"
|
|
SharedTargetDomain = "domain"
|
|
)
|
|
|
|
// sharing resource between projects/domains
|
|
type SSharedResource struct {
|
|
SResourceBase
|
|
|
|
Id int64 `primary:"true" auto_increment:"true"`
|
|
|
|
ResourceType string `width:"32" charset:"ascii" nullable:"false" json:"resource_type"`
|
|
ResourceId string `width:"128" charset:"ascii" nullable:"false" index:"true" json:"resource_id"`
|
|
// OwnerProjectId string `width:"128" charset:"ascii" nullable:"false" index:"true" json:"owner_project_id"`
|
|
TargetProjectId string `width:"128" charset:"ascii" nullable:"false" index:"true" json:"target_project_id"`
|
|
|
|
TargetType string `width:"8" charset:"ascii" default:"project" nullable:"false" json:"target_type"`
|
|
}
|
|
|
|
type SSharedResourceManager struct {
|
|
SResourceBaseManager
|
|
}
|
|
|
|
var SharedResourceManager *SSharedResourceManager
|
|
|
|
func init() {
|
|
SharedResourceManager = &SSharedResourceManager{
|
|
SResourceBaseManager: NewResourceBaseManager(
|
|
SSharedResource{},
|
|
"shared_resources_tbl",
|
|
"shared_resource",
|
|
"shared_resources",
|
|
),
|
|
}
|
|
}
|
|
|
|
func (manager *SSharedResourceManager) CleanModelShares(ctx context.Context, userCred mcclient.TokenCredential, model ISharableBaseModel) error {
|
|
var err error
|
|
resScope := model.GetModelManager().ResourceScope()
|
|
switch resScope {
|
|
case rbacscope.ScopeProject:
|
|
_, err = manager.shareToTarget(ctx, userCred, model, SharedTargetProject, nil, nil, nil)
|
|
if err != nil {
|
|
return errors.Wrap(err, "remove shared project")
|
|
}
|
|
_, err = manager.shareToTarget(ctx, userCred, model, SharedTargetDomain, nil, nil, nil)
|
|
if err != nil {
|
|
return errors.Wrap(err, "remove shared domain")
|
|
}
|
|
case rbacscope.ScopeDomain:
|
|
_, err = manager.shareToTarget(ctx, userCred, model, SharedTargetDomain, nil, nil, nil)
|
|
if err != nil {
|
|
return errors.Wrap(err, "remove shared domain")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (manager *SSharedResourceManager) shareToTarget(
|
|
ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
model ISharableBaseModel,
|
|
targetType string,
|
|
targetIds []string,
|
|
candidateIds []string,
|
|
requireDomainIds []string,
|
|
) ([]string, error) {
|
|
var requireScope rbacscope.TRbacScope
|
|
resScope := model.GetModelManager().ResourceScope()
|
|
switch resScope {
|
|
case rbacscope.ScopeUser:
|
|
switch targetType {
|
|
case SharedTargetDomain:
|
|
// should have system-level privileges
|
|
requireScope = rbacscope.ScopeSystem
|
|
case SharedTargetProject:
|
|
requireScope = rbacscope.ScopeDomain
|
|
}
|
|
case rbacscope.ScopeProject:
|
|
switch targetType {
|
|
case SharedTargetProject:
|
|
// should have domain-level privileges
|
|
// cannot share to a project across domain
|
|
requireScope = rbacscope.ScopeDomain
|
|
case SharedTargetDomain:
|
|
// should have system-level privileges
|
|
requireScope = rbacscope.ScopeSystem
|
|
}
|
|
case rbacscope.ScopeDomain:
|
|
switch targetType {
|
|
case SharedTargetDomain:
|
|
// should have system-level privileges
|
|
requireScope = rbacscope.ScopeSystem
|
|
case SharedTargetProject:
|
|
if len(targetIds) > 0 {
|
|
return nil, errors.Wrap(httperrors.ErrNotSupported, "cannot share a domain resource to specific project")
|
|
}
|
|
}
|
|
default:
|
|
return nil, errors.Wrap(httperrors.ErrNotSupported, "cannot share a non-project/domain resource")
|
|
}
|
|
|
|
srs := make([]SSharedResource, 0)
|
|
q := SharedResourceManager.Query()
|
|
q = q.Equals("resource_type", model.Keyword())
|
|
q = q.Equals("resource_id", model.GetId())
|
|
q = q.Equals("target_type", targetType)
|
|
err := FetchModelObjects(SharedResourceManager, q, &srs)
|
|
if err != nil && errors.Cause(err) != sql.ErrNoRows {
|
|
return nil, errors.Wrap(err, "Fetch shared project")
|
|
}
|
|
srsMap := make(map[string]*SSharedResource)
|
|
_existIds := make([]string, len(srs))
|
|
for i := 0; i < len(srs); i++ {
|
|
_existIds[i] = srs[i].TargetProjectId
|
|
srsMap[srs[i].TargetProjectId] = &srs[i]
|
|
}
|
|
existIds := stringutils2.NewSortedStrings(_existIds)
|
|
|
|
newIds := stringutils2.NewSortedStrings([]string{})
|
|
modelOwnerId := model.GetOwnerId()
|
|
for i := 0; i < len(targetIds); i++ {
|
|
switch targetType {
|
|
case SharedTargetProject:
|
|
tenant, err := DefaultProjectFetcher(ctx, targetIds[i], "")
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "fetch tenant %s error", targetIds[i])
|
|
}
|
|
if tenant.DomainId != modelOwnerId.GetProjectDomainId() {
|
|
return nil, errors.Wrap(httperrors.ErrBadRequest, "can't shared project to other domain")
|
|
}
|
|
if tenant.GetId() == modelOwnerId.GetProjectId() {
|
|
// ignore self project
|
|
continue
|
|
// return nil, errors.Wrap(httperrors.ErrBadRequest, "can't share to self project")
|
|
}
|
|
newIds = stringutils2.Append(newIds, tenant.GetId())
|
|
case SharedTargetDomain:
|
|
domain, err := DefaultDomainFetcher(ctx, targetIds[i])
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "fetch domain %s error", targetIds[i])
|
|
}
|
|
if domain.GetId() == modelOwnerId.GetProjectDomainId() {
|
|
// ignore self domain
|
|
continue
|
|
// return nil, errors.Wrapf(httperrors.ErrBadRequest, "can't share to self domain %s", modelOwnerId.GetProjectDomainId())
|
|
}
|
|
if len(candidateIds) > 0 && !utils.IsInStringArray(domain.GetId(), candidateIds) {
|
|
return nil, errors.Wrapf(httperrors.ErrForbidden, "share target domain %s not in candidate list %s", domain.GetId(), candidateIds)
|
|
}
|
|
newIds = stringutils2.Append(newIds, domain.GetId())
|
|
}
|
|
}
|
|
delIds, keepIds, addIds := stringutils2.Split(existIds, newIds)
|
|
|
|
if len(delIds) == 0 && len(addIds) == 0 {
|
|
return keepIds, nil
|
|
}
|
|
|
|
if targetType == SharedTargetDomain && len(requireDomainIds) > 0 && len(delIds) > 0 {
|
|
for _, delId := range delIds {
|
|
if utils.IsInStringArray(delId, requireDomainIds) {
|
|
return nil, errors.Wrapf(httperrors.ErrForbidden, "domain %s is required to share", delId)
|
|
}
|
|
}
|
|
}
|
|
|
|
allowScope, policyTags := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.KeywordPlural(), policy.PolicyActionPerform, "public")
|
|
if requireScope.HigherThan(allowScope) {
|
|
return nil, errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", requireScope, allowScope)
|
|
}
|
|
|
|
err = objectConfirmPolicyTags(ctx, model, policyTags)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "objectConfirmPolicyTags")
|
|
}
|
|
|
|
for _, targetId := range delIds {
|
|
sr := srsMap[targetId]
|
|
if err := sr.Delete(ctx, userCred); err != nil {
|
|
return nil, errors.Wrap(err, "delete")
|
|
}
|
|
}
|
|
for _, targetId := range addIds {
|
|
sharedResource := new(SSharedResource)
|
|
sharedResource.ResourceType = model.Keyword()
|
|
sharedResource.ResourceId = model.GetId()
|
|
sharedResource.TargetProjectId = targetId
|
|
sharedResource.TargetType = targetType
|
|
if insetErr := SharedResourceManager.TableSpec().Insert(ctx, sharedResource); insetErr != nil {
|
|
return nil, httperrors.NewInternalServerError("Insert shared resource failed %s", insetErr)
|
|
}
|
|
}
|
|
|
|
keepIds = append(keepIds, addIds...)
|
|
return keepIds, nil
|
|
}
|