mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-01 15:07:17 +08:00
tag filter for rbac
This commit is contained in:
@@ -21,8 +21,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/tagutils"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
@@ -38,6 +36,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
"yunion.io/x/onecloud/pkg/util/tagutils"
|
||||
)
|
||||
|
||||
func createPolicy(s *mcclient.ClientSession, name string, policy string, domain string, enabled bool, disabled bool, desc string, scope string, isSystem *bool, objectags, projecttags, domaintags tagutils.TTagSet) error {
|
||||
|
||||
@@ -16,11 +16,11 @@ package identity
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/util/tagutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/tagutils"
|
||||
)
|
||||
|
||||
type IdentityBaseResourceCreateInput struct {
|
||||
@@ -159,6 +159,9 @@ type ProjectListInput struct {
|
||||
|
||||
// 过滤出指定用户或者组可以加入的项目
|
||||
Jointable *bool `json:"jointable"`
|
||||
|
||||
// project tags filter imposed by policy
|
||||
PolicyProjectTags tagutils.TTagSetList `json:"policy_project_tags"`
|
||||
}
|
||||
|
||||
type DomainListInput struct {
|
||||
@@ -171,6 +174,9 @@ type DomainListInput struct {
|
||||
|
||||
// 按IDP_ENTITY_ID过滤
|
||||
IdpEntityId string `json:"idp_entity_id"`
|
||||
|
||||
// domain tags filter imposed by policy
|
||||
PolicyDomainTags tagutils.TTagSetList `json:"policy_domain_tags"`
|
||||
}
|
||||
|
||||
type UserListInput struct {
|
||||
|
||||
@@ -1301,11 +1301,11 @@ func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils
|
||||
}
|
||||
}
|
||||
|
||||
var requireTags tagutils.TTagSet
|
||||
var policyResult rbacutils.SPolicyResult
|
||||
if consts.IsRbacEnabled() {
|
||||
requireTags, err = isClassRbacAllowed(ctx, dispatcher.modelManager, userCred, ownerId, policy.PolicyActionCreate)
|
||||
policyResult, err = isClassRbacAllowed(ctx, dispatcher.modelManager, userCred, ownerId, policy.PolicyActionCreate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "isClassRbacAllowed")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1313,12 +1313,7 @@ func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils
|
||||
ctx = InitPendingUsagesInContext(ctx)
|
||||
}
|
||||
|
||||
if len(requireTags) > 0 {
|
||||
// add requireTags to require data
|
||||
for _, tag := range requireTags {
|
||||
data.(*jsonutils.JSONDict).Add(jsonutils.NewString(tag.Value), "__meta__", tag.Key)
|
||||
}
|
||||
}
|
||||
data.(*jsonutils.JSONDict).Update(policyResult.Json())
|
||||
|
||||
model, err := DoCreate(dispatcher.modelManager, ctx, userCred, query, data, ownerId)
|
||||
if err != nil {
|
||||
@@ -1395,19 +1390,15 @@ func (dispatcher *DBModelDispatcher) BatchCreate(ctx context.Context, query json
|
||||
}
|
||||
}
|
||||
|
||||
var requireTags tagutils.TTagSet
|
||||
var policyResult rbacutils.SPolicyResult
|
||||
if consts.IsRbacEnabled() {
|
||||
requireTags, err = isClassRbacAllowed(ctx, manager, userCred, ownerId, policy.PolicyActionCreate)
|
||||
policyResult, err = isClassRbacAllowed(ctx, manager, userCred, ownerId, policy.PolicyActionCreate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "isClassRbacAllowd")
|
||||
}
|
||||
}
|
||||
|
||||
if len(requireTags) > 0 {
|
||||
for _, tag := range requireTags {
|
||||
data.(*jsonutils.JSONDict).Add(jsonutils.NewString(tag.Value), "__meta__", tag.Key)
|
||||
}
|
||||
}
|
||||
data.(*jsonutils.JSONDict).Update(policyResult.Json())
|
||||
|
||||
type sCreateResult struct {
|
||||
model IModel
|
||||
@@ -1608,6 +1599,7 @@ func reflectDispatcherInternal(
|
||||
params = []interface{}{ctx, userCred, query, data}
|
||||
}
|
||||
|
||||
var result rbacutils.SPolicyResult
|
||||
if consts.IsRbacEnabled() {
|
||||
if model == nil {
|
||||
ownerId, err := fetchOwnerId(ctx, dispatcher.modelManager, userCred, data)
|
||||
@@ -1619,30 +1611,12 @@ func reflectDispatcherInternal(
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
err := isObjectRbacAllowed(ctx, model, userCred, operator, spec)
|
||||
var err error
|
||||
result, err = isObjectRbacAllowedResult(ctx, model, userCred, operator, spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
allowFuncName := "Allow" + funcName
|
||||
allowFuncValue := modelValue.MethodByName(allowFuncName)
|
||||
if !allowFuncValue.IsValid() || allowFuncValue.IsNil() {
|
||||
return nil, httperrors.NewActionNotFoundError("%s allow %s %s not found",
|
||||
dispatcher.Keyword(), operator, spec)
|
||||
}
|
||||
|
||||
outs, err := callFunc(allowFuncValue, allowFuncName, params...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(outs) != 1 {
|
||||
return nil, httperrors.NewInternalServerError("Invald %s return value", allowFuncName)
|
||||
}
|
||||
|
||||
if !outs[0].Bool() {
|
||||
return nil, httperrors.NewForbiddenError("%s not allow to %s %s", dispatcher.Keyword(), operator, spec)
|
||||
}
|
||||
}
|
||||
|
||||
outs, err := callFunc(funcValue, funcName, params...)
|
||||
@@ -1657,6 +1631,16 @@ func reflectDispatcherInternal(
|
||||
if !gotypes.IsNil(errVal) {
|
||||
return nil, errVal.(error)
|
||||
} else {
|
||||
if model != nil {
|
||||
if _, ok := model.(IStandaloneModel); ok {
|
||||
Metadata.rawSetValues(ctx, model.Keyword(), model.GetId(), tagutils.Tagset2MapString(result.ObjectTags.Flattern()), false, "")
|
||||
if model.Keyword() == "project" {
|
||||
Metadata.rawSetValues(ctx, model.Keyword(), model.GetId(), tagutils.Tagset2MapString(result.ProjectTags.Flattern()), false, "")
|
||||
} else if model.Keyword() == "domain" {
|
||||
Metadata.rawSetValues(ctx, model.Keyword(), model.GetId(), tagutils.Tagset2MapString(result.DomainTags.Flattern()), false, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
if gotypes.IsNil(resVal.Interface()) {
|
||||
return nil, nil
|
||||
} else {
|
||||
@@ -1718,10 +1702,11 @@ func (dispatcher *DBModelDispatcher) Update(ctx context.Context, idStr string, q
|
||||
}
|
||||
|
||||
if consts.IsRbacEnabled() {
|
||||
err := isObjectRbacAllowed(ctx, model, userCred, policy.PolicyActionUpdate)
|
||||
result, err := isObjectRbacAllowedResult(ctx, model, userCred, policy.PolicyActionUpdate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.(*jsonutils.JSONDict).Update(result.Json())
|
||||
} else if !model.AllowUpdateItem(ctx, userCred) {
|
||||
return nil, httperrors.NewForbiddenError("Not allow to update item")
|
||||
}
|
||||
|
||||
@@ -117,18 +117,15 @@ func (manager *SDomainizedResourceBaseManager) ListItemFilter(
|
||||
q = q.In("domain_id", subq)
|
||||
}
|
||||
if len(query.DomainTags) > 0 {
|
||||
meta := SMetadataResourceBaseModelManager{}
|
||||
subq := meta.objIdQueryWithTags("domain", nil, query.DomainTags).SubQuery()
|
||||
subq := objIdQueryWithTags("domain", nil, query.DomainTags).SubQuery()
|
||||
q = q.In("domain_id", subq)
|
||||
}
|
||||
if len(query.NoDomainTags) > 0 {
|
||||
meta := SMetadataResourceBaseModelManager{}
|
||||
subq := meta.objIdQueryWithTags("domain", nil, query.NoDomainTags).SubQuery()
|
||||
subq := objIdQueryWithTags("domain", nil, query.NoDomainTags).SubQuery()
|
||||
q = q.NotIn("domain_id", subq)
|
||||
}
|
||||
if len(query.PolicyDomainTags) > 0 {
|
||||
meta := SMetadataResourceBaseModelManager{}
|
||||
subq := meta.objIdQueryWithTags("domain", nil, query.PolicyDomainTags).SubQuery()
|
||||
subq := objIdQueryWithTags("domain", nil, query.PolicyDomainTags).SubQuery()
|
||||
q = q.In("domain_id", subq)
|
||||
}
|
||||
return q, nil
|
||||
|
||||
@@ -17,8 +17,6 @@ package db
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/tagutils"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/sqlchemy"
|
||||
@@ -27,11 +25,16 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/tagutils"
|
||||
)
|
||||
|
||||
type SMetadataResourceBaseModelManager struct{}
|
||||
|
||||
func (meta *SMetadataResourceBaseModelManager) objIdQueryWithTags(modelName string, oTags tagutils.TTagSet, oMoreTags tagutils.TTagSetList) *sqlchemy.SQuery {
|
||||
func ObjIdQueryWithTags(modelName string, oMoreTags tagutils.TTagSetList) *sqlchemy.SQuery {
|
||||
return objIdQueryWithTags(modelName, nil, oMoreTags)
|
||||
}
|
||||
|
||||
func objIdQueryWithTags(modelName string, oTags tagutils.TTagSet, oMoreTags tagutils.TTagSetList) *sqlchemy.SQuery {
|
||||
metadataResQ := Metadata.Query().Equals("obj_type", modelName).SubQuery()
|
||||
|
||||
queries := make([]sqlchemy.IQuery, 0)
|
||||
@@ -82,7 +85,7 @@ func (meta *SMetadataResourceBaseModelManager) ListItemFilter(
|
||||
input apis.MetadataResourceListInput,
|
||||
) *sqlchemy.SQuery {
|
||||
if len(input.Tags) > 0 || len(input.ObjTags) > 0 {
|
||||
sq := meta.objIdQueryWithTags(manager.Keyword(), input.Tags, input.ObjTags)
|
||||
sq := objIdQueryWithTags(manager.Keyword(), input.Tags, input.ObjTags)
|
||||
if sq != nil {
|
||||
sqq := sq.SubQuery()
|
||||
q = q.Join(sqq, sqlchemy.Equals(q.Field("id"), sqq.Field("obj_id")))
|
||||
@@ -91,7 +94,7 @@ func (meta *SMetadataResourceBaseModelManager) ListItemFilter(
|
||||
}
|
||||
|
||||
if len(input.PolicyObjectTags) > 0 {
|
||||
sq := meta.objIdQueryWithTags(manager.Keyword(), nil, input.PolicyObjectTags)
|
||||
sq := objIdQueryWithTags(manager.Keyword(), nil, input.PolicyObjectTags)
|
||||
if sq != nil {
|
||||
sqq := sq.SubQuery()
|
||||
q = q.Join(sqq, sqlchemy.Equals(q.Field("id"), sqq.Field("obj_id")))
|
||||
@@ -99,7 +102,7 @@ func (meta *SMetadataResourceBaseModelManager) ListItemFilter(
|
||||
}
|
||||
|
||||
if len(input.NoTags) > 0 || len(input.NoObjTags) > 0 {
|
||||
sq := meta.objIdQueryWithTags(manager.Keyword(), input.NoTags, input.NoObjTags)
|
||||
sq := objIdQueryWithTags(manager.Keyword(), input.NoTags, input.NoObjTags)
|
||||
if sq != nil {
|
||||
q = q.Filter(sqlchemy.NotIn(q.Field("id"), sq.SubQuery()))
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
@@ -27,18 +26,26 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/util/tagutils"
|
||||
)
|
||||
|
||||
func objectConfirmPolicyTags(ctx context.Context, userCred mcclient.TokenCredential, model IModel, result rbacutils.SPolicyResult) error {
|
||||
func objectConfirmPolicyTags(ctx context.Context, model IModel, result rbacutils.SPolicyResult) error {
|
||||
if _, ok := model.(IStandaloneModel); !ok {
|
||||
// a plain resource
|
||||
return nil
|
||||
}
|
||||
// now, its is a system resource
|
||||
resTags, err := model.(IStandaloneModel).GetAllMetadata(userCred)
|
||||
|
||||
resTagsMap, err := Metadata.rawGetAll(model.Keyword(), model.GetId(), nil, "")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Standalone model GetAllMetadata")
|
||||
}
|
||||
if !result.ObjectTags.Contains(tagutils.Map2Tagset(resTags)) {
|
||||
return httperrors.NewNotSufficientPrivilegeError("resource tags not match (%s,require:%s)", jsonutils.Marshal(resTags), result.ObjectTags)
|
||||
resTags := tagutils.Map2Tagset(resTagsMap)
|
||||
if model.Keyword() == "domain" && !result.DomainTags.Contains(resTags) {
|
||||
return httperrors.NewNotSufficientPrivilegeError("resource (domain) tags not match (tags:%s,require:%s)", jsonutils.Marshal(resTags), jsonutils.Marshal(result.DomainTags))
|
||||
}
|
||||
if model.Keyword() == "project" && !result.ProjectTags.Contains(resTags) {
|
||||
return httperrors.NewNotSufficientPrivilegeError("resource (project) tags not match (tags:%s,require:%s)", jsonutils.Marshal(resTags), jsonutils.Marshal(result.ProjectTags))
|
||||
}
|
||||
if !result.ObjectTags.Contains(resTags) {
|
||||
return httperrors.NewNotSufficientPrivilegeError("resource tags not match (tags:%s,require:%s)", jsonutils.Marshal(resTags), jsonutils.Marshal(result.ObjectTags))
|
||||
}
|
||||
if _, ok := model.(IDomainLevelModel); !ok {
|
||||
// a system level resource
|
||||
@@ -71,44 +78,38 @@ func objectConfirmPolicyTags(ctx context.Context, userCred mcclient.TokenCredent
|
||||
return nil
|
||||
}
|
||||
|
||||
func classConfirmPolicyTags(ctx context.Context, userCred mcclient.TokenCredential, manager IModelManager, objectOwnerId mcclient.IIdentityProvider, result rbacutils.SPolicyResult) (tagutils.TTagSet, error) {
|
||||
func classConfirmPolicyTags(ctx context.Context, manager IModelManager, objectOwnerId mcclient.IIdentityProvider, result rbacutils.SPolicyResult) error {
|
||||
if _, ok := manager.(IStandaloneModelManager); !ok {
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
// now, the manager is a standalone model manager
|
||||
requireResourceTags := result.ObjectTags.Flattern()
|
||||
if _, ok := manager.(IDomainLevelModelManager); !ok {
|
||||
// a system level resource manager
|
||||
return requireResourceTags, nil
|
||||
return nil
|
||||
}
|
||||
// now the manager is a domain level manager, we should check domain tags
|
||||
if objectOwnerId != nil && objectOwnerId.GetProjectDomainId() != "" {
|
||||
domain, err := TenantCacheManager.FetchDomainById(ctx, objectOwnerId.GetProjectDomainId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "TenantCacheManager.FetchDomainById")
|
||||
return errors.Wrap(err, "TenantCacheManager.FetchDomainById")
|
||||
}
|
||||
if !result.DomainTags.Contains(domain.GetTags()) {
|
||||
return nil, httperrors.NewNotSufficientPrivilegeError("domain tags not match (%s,require:%s)", jsonutils.Marshal(domain.GetTags()), result.DomainTags)
|
||||
return httperrors.NewNotSufficientPrivilegeError("domain tags not match (%s,require:%s)", jsonutils.Marshal(domain.GetTags()), result.DomainTags)
|
||||
}
|
||||
}
|
||||
if _, ok := manager.(IVirtualModelManager); !ok {
|
||||
// a domain level resource manager
|
||||
return requireResourceTags, nil
|
||||
return nil
|
||||
}
|
||||
// now the manager is project level manager, we should check project tags
|
||||
if objectOwnerId != nil && objectOwnerId.GetProjectId() != "" {
|
||||
project, err := TenantCacheManager.FetchTenantById(ctx, objectOwnerId.GetProjectId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "TenantCacheManager.FetchTenantById")
|
||||
return errors.Wrap(err, "TenantCacheManager.FetchTenantById")
|
||||
}
|
||||
if !result.ProjectTags.Contains(project.GetTags()) {
|
||||
return nil, httperrors.NewNotSufficientPrivilegeError("project tags not match (%s,require:%s)", jsonutils.Marshal(project.GetTags()), result.ProjectTags)
|
||||
return httperrors.NewNotSufficientPrivilegeError("project tags not match (%s,require:%s)", jsonutils.Marshal(project.GetTags()), result.ProjectTags)
|
||||
}
|
||||
}
|
||||
return requireResourceTags, nil
|
||||
}
|
||||
|
||||
func filterByTagFilters(q *sqlchemy.SQuery, result rbacutils.SPolicyResult) *sqlchemy.SQuery {
|
||||
// to do
|
||||
return q
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -111,15 +111,17 @@ func (manager *SProjectizedResourceBaseManager) ListItemFilter(
|
||||
q = q.In("tenant_id", subq)
|
||||
}
|
||||
if len(query.ProjectTags) > 0 {
|
||||
meta := SMetadataResourceBaseModelManager{}
|
||||
subq := meta.objIdQueryWithTags("project", nil, query.ProjectTags).SubQuery()
|
||||
subq := objIdQueryWithTags("project", nil, query.ProjectTags).SubQuery()
|
||||
q = q.In("tenant_id", subq)
|
||||
}
|
||||
if len(query.NoProjectTags) > 0 {
|
||||
meta := SMetadataResourceBaseModelManager{}
|
||||
subq := meta.objIdQueryWithTags("project", nil, query.NoProjectTags).SubQuery()
|
||||
subq := objIdQueryWithTags("project", nil, query.NoProjectTags).SubQuery()
|
||||
q = q.NotIn("tenant_id", subq)
|
||||
}
|
||||
if len(query.PolicyProjectTags) > 0 {
|
||||
subq := objIdQueryWithTags("project", nil, query.PolicyProjectTags).SubQuery()
|
||||
q = q.In("tenant_id", subq)
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
|
||||
+27
-14
@@ -18,13 +18,13 @@ import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"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/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/tagutils"
|
||||
)
|
||||
|
||||
func IsObjectRbacAllowed(ctx context.Context, model IModel, userCred mcclient.TokenCredential, action string, extra ...string) error {
|
||||
@@ -32,6 +32,11 @@ func IsObjectRbacAllowed(ctx context.Context, model IModel, userCred mcclient.To
|
||||
}
|
||||
|
||||
func isObjectRbacAllowed(ctx context.Context, model IModel, userCred mcclient.TokenCredential, action string, extra ...string) error {
|
||||
_, err := isObjectRbacAllowedResult(ctx, model, userCred, action, extra...)
|
||||
return err
|
||||
}
|
||||
|
||||
func isObjectRbacAllowedResult(ctx context.Context, model IModel, userCred mcclient.TokenCredential, action string, extra ...string) (rbacutils.SPolicyResult, error) {
|
||||
manager := model.GetModelManager()
|
||||
objOwnerId := model.GetOwnerId()
|
||||
|
||||
@@ -74,10 +79,14 @@ func isObjectRbacAllowed(ctx context.Context, model IModel, userCred mcclient.To
|
||||
|
||||
scope, result := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), manager.KeywordPlural(), action, extra...)
|
||||
|
||||
if !requireScope.HigherThan(scope) {
|
||||
return objectConfirmPolicyTags(ctx, userCred, model, result)
|
||||
if result.Result.IsAllow() && !requireScope.HigherThan(scope) {
|
||||
err := objectConfirmPolicyTags(ctx, model, result)
|
||||
if err != nil {
|
||||
return rbacutils.PolicyDeny, errors.Wrap(err, "objectConfirmPolicyTags")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
return httperrors.NewForbiddenError("not enough privilege (require:%s,allow:%s:resource:%s) [tags:%s]", requireScope, scope, resScope, result.String())
|
||||
return rbacutils.PolicyDeny, httperrors.NewForbiddenError("not enough privilege (require:%s,allow:%s:resource:%s) [tags:%s]", requireScope, scope, resScope, result.String())
|
||||
}
|
||||
|
||||
func isJointObjectRbacAllowed(ctx context.Context, item IJointModel, userCred mcclient.TokenCredential, action string, extra ...string) error {
|
||||
@@ -89,7 +98,7 @@ func isJointObjectRbacAllowed(ctx context.Context, item IJointModel, userCred mc
|
||||
return err1
|
||||
}
|
||||
|
||||
func isClassRbacAllowed(ctx context.Context, manager IModelManager, userCred mcclient.TokenCredential, objOwnerId mcclient.IIdentityProvider, action string, extra ...string) (tagutils.TTagSet, error) {
|
||||
func isClassRbacAllowed(ctx context.Context, manager IModelManager, userCred mcclient.TokenCredential, objOwnerId mcclient.IIdentityProvider, action string, extra ...string) (rbacutils.SPolicyResult, error) {
|
||||
var ownerId mcclient.IIdentityProvider
|
||||
if userCred != nil {
|
||||
ownerId = userCred
|
||||
@@ -128,10 +137,14 @@ func isClassRbacAllowed(ctx context.Context, manager IModelManager, userCred mcc
|
||||
|
||||
allowScope, result := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), manager.KeywordPlural(), action, extra...)
|
||||
|
||||
if !requireScope.HigherThan(allowScope) {
|
||||
return classConfirmPolicyTags(ctx, userCred, manager, objOwnerId, result)
|
||||
if result.Result.IsAllow() && !requireScope.HigherThan(allowScope) {
|
||||
err := classConfirmPolicyTags(ctx, manager, objOwnerId, result)
|
||||
if err != nil {
|
||||
return rbacutils.PolicyDeny, errors.Wrap(err, "classConfirmPolicyTags")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
return nil, httperrors.NewForbiddenError("not enough privilege (require:%s,allow:%s)", requireScope, allowScope)
|
||||
return rbacutils.PolicyDeny, httperrors.NewForbiddenError("not enough privilege (require:%s,allow:%s)", requireScope, allowScope)
|
||||
}
|
||||
|
||||
type IResource interface {
|
||||
@@ -200,7 +213,7 @@ func IsAllowGet(ctx context.Context, scope rbacutils.TRbacScope, userCred mcclie
|
||||
return false
|
||||
}
|
||||
result := userCred.IsAllow(scope, consts.GetServiceType(), obj.KeywordPlural(), policy.PolicyActionGet)
|
||||
err := objectConfirmPolicyTags(ctx, userCred, obj, result)
|
||||
err := objectConfirmPolicyTags(ctx, obj, result)
|
||||
if err != nil {
|
||||
log.Errorf("IsAllowGet %s", err)
|
||||
return false
|
||||
@@ -226,7 +239,7 @@ func IsAllowGetSpec(ctx context.Context, scope rbacutils.TRbacScope, userCred mc
|
||||
return false
|
||||
}
|
||||
result := userCred.IsAllow(scope, consts.GetServiceType(), obj.KeywordPlural(), policy.PolicyActionGet, spec)
|
||||
err := objectConfirmPolicyTags(ctx, userCred, obj, result)
|
||||
err := objectConfirmPolicyTags(ctx, obj, result)
|
||||
if err != nil {
|
||||
log.Errorf("IsAllowGetSpec %s", err)
|
||||
return false
|
||||
@@ -252,7 +265,7 @@ func IsAllowPerform(ctx context.Context, scope rbacutils.TRbacScope, userCred mc
|
||||
return false
|
||||
}
|
||||
result := userCred.IsAllow(scope, consts.GetServiceType(), obj.KeywordPlural(), policy.PolicyActionPerform, action)
|
||||
err := objectConfirmPolicyTags(ctx, userCred, obj, result)
|
||||
err := objectConfirmPolicyTags(ctx, obj, result)
|
||||
if err != nil {
|
||||
log.Errorf("IsAllowPerform %s", err)
|
||||
return false
|
||||
@@ -278,7 +291,7 @@ func IsAllowUpdate(ctx context.Context, scope rbacutils.TRbacScope, userCred mcc
|
||||
return false
|
||||
}
|
||||
result := userCred.IsAllow(scope, consts.GetServiceType(), obj.KeywordPlural(), policy.PolicyActionUpdate)
|
||||
err := objectConfirmPolicyTags(ctx, userCred, obj, result)
|
||||
err := objectConfirmPolicyTags(ctx, obj, result)
|
||||
if err != nil {
|
||||
log.Errorf("IsAllowUpdate %s", err)
|
||||
return false
|
||||
@@ -304,7 +317,7 @@ func IsAllowUpdateSpec(ctx context.Context, scope rbacutils.TRbacScope, userCred
|
||||
return false
|
||||
}
|
||||
result := userCred.IsAllow(scope, consts.GetServiceType(), obj.KeywordPlural(), policy.PolicyActionUpdate, spec)
|
||||
err := objectConfirmPolicyTags(ctx, userCred, obj, result)
|
||||
err := objectConfirmPolicyTags(ctx, obj, result)
|
||||
if err != nil {
|
||||
log.Errorf("IsAllowUpdateSpec %s", err)
|
||||
return false
|
||||
@@ -330,7 +343,7 @@ func IsAllowDelete(ctx context.Context, scope rbacutils.TRbacScope, userCred mcc
|
||||
return false
|
||||
}
|
||||
result := userCred.IsAllow(scope, consts.GetServiceType(), obj.KeywordPlural(), policy.PolicyActionDelete)
|
||||
err := objectConfirmPolicyTags(ctx, userCred, obj, result)
|
||||
err := objectConfirmPolicyTags(ctx, obj, result)
|
||||
if err != nil {
|
||||
log.Errorf("IsAllowDelete %s", err)
|
||||
return false
|
||||
|
||||
@@ -478,7 +478,7 @@ func SharablePerformPublic(model ISharableBaseModel, ctx context.Context, userCr
|
||||
return errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", requireScope, allowScope)
|
||||
}
|
||||
|
||||
err = objectConfirmPolicyTags(ctx, userCred, model, policyTags)
|
||||
err = objectConfirmPolicyTags(ctx, model, policyTags)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "objectConfirmPolicyTags")
|
||||
}
|
||||
@@ -524,7 +524,7 @@ func SharablePerformPrivate(model ISharableBaseModel, ctx context.Context, userC
|
||||
return errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", requireScope, allowScope)
|
||||
}
|
||||
|
||||
err := objectConfirmPolicyTags(ctx, userCred, model, policyTags)
|
||||
err := objectConfirmPolicyTags(ctx, model, policyTags)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "objectConfirmPolicyTags")
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ func (manager *SSharedResourceManager) shareToTarget(
|
||||
return nil, errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", requireScope, allowScope)
|
||||
}
|
||||
|
||||
err = objectConfirmPolicyTags(ctx, userCred, model, policyTags)
|
||||
err = objectConfirmPolicyTags(ctx, model, policyTags)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "objectConfirmPolicyTags")
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/stringutils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
@@ -30,6 +31,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/tagutils"
|
||||
)
|
||||
|
||||
type UUIDGenerator func() string
|
||||
@@ -397,6 +399,12 @@ func (model *SStandaloneAnonResourceBase) PerformSetUserMetadata(ctx context.Con
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type sPolicyTags struct {
|
||||
PolicyObjectTags tagutils.TTagSetList `json:"policy_object_tags"`
|
||||
PolicyProjectTags tagutils.TTagSetList `json:"policy_project_tags"`
|
||||
PolicyDomainTags tagutils.TTagSetList `json:"policy_domain_tags"`
|
||||
}
|
||||
|
||||
func (model *SStandaloneAnonResourceBase) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
model.SResourceBase.PostUpdate(ctx, userCred, query, data)
|
||||
|
||||
@@ -405,6 +413,8 @@ func (model *SStandaloneAnonResourceBase) PostUpdate(ctx context.Context, userCr
|
||||
if err == nil {
|
||||
model.PerformMetadata(ctx, userCred, nil, meta)
|
||||
}
|
||||
|
||||
model.applyPolicyTags(ctx, userCred, data)
|
||||
}
|
||||
|
||||
func (model *SStandaloneAnonResourceBase) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
@@ -415,6 +425,22 @@ func (model *SStandaloneAnonResourceBase) PostCreate(ctx context.Context, userCr
|
||||
if err == nil {
|
||||
model.PerformMetadata(ctx, userCred, nil, meta)
|
||||
}
|
||||
|
||||
model.applyPolicyTags(ctx, userCred, data)
|
||||
}
|
||||
|
||||
func (model *SStandaloneAnonResourceBase) applyPolicyTags(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) {
|
||||
tags := sPolicyTags{}
|
||||
data.Unmarshal(&tags)
|
||||
log.Debugf("applyPolicyTags: %s", jsonutils.Marshal(tags))
|
||||
if len(tags.PolicyObjectTags) > 0 {
|
||||
model.PerformMetadata(ctx, userCred, nil, tagutils.Tagset2MapString(tags.PolicyObjectTags.Flattern()))
|
||||
}
|
||||
if model.Keyword() == "project" && len(tags.PolicyProjectTags) > 0 {
|
||||
model.PerformMetadata(ctx, userCred, nil, tagutils.Tagset2MapString(tags.PolicyProjectTags.Flattern()))
|
||||
} else if model.Keyword() == "domain" && len(tags.PolicyDomainTags) > 0 {
|
||||
model.PerformMetadata(ctx, userCred, nil, tagutils.Tagset2MapString(tags.PolicyDomainTags.Flattern()))
|
||||
}
|
||||
}
|
||||
|
||||
func (model *SStandaloneAnonResourceBase) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
|
||||
|
||||
@@ -407,7 +407,7 @@ func (model *SVirtualResourceBase) PerformChangeOwner(ctx context.Context, userC
|
||||
return nil, errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", requireScope, allowScope)
|
||||
}
|
||||
|
||||
err = objectConfirmPolicyTags(ctx, userCred, model, policyTags)
|
||||
err = objectConfirmPolicyTags(ctx, model, policyTags)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "objectConfirmPolicyTags")
|
||||
}
|
||||
|
||||
@@ -188,6 +188,12 @@ func (manager *SDomainManager) ListItemFilter(
|
||||
}
|
||||
q = q.NotEquals("id", api.KeystoneDomainRoot)
|
||||
|
||||
if len(query.PolicyDomainTags) > 0 {
|
||||
// apply policy imposed domain tag filters
|
||||
subq := db.ObjIdQueryWithTags("domain", query.PolicyDomainTags).SubQuery()
|
||||
q = q.In("id", subq)
|
||||
}
|
||||
|
||||
if query.Enabled != nil {
|
||||
if *query.Enabled {
|
||||
q = q.IsTrue("enabled")
|
||||
|
||||
@@ -95,7 +95,7 @@ type SPolicy struct {
|
||||
DomainTags tagutils.TTagSet `nullable:"true" list:"user" update:"admin" create:"admin_optional"`
|
||||
|
||||
// 匹配的资源标签
|
||||
ResourceTags tagutils.TTagSet `nullable:"true" list:"user" update:"domain" create:"domain_optional"`
|
||||
ObjectTags tagutils.TTagSet `nullable:"true" list:"user" update:"domain" create:"domain_optional"`
|
||||
}
|
||||
|
||||
func (manager *SPolicyManager) InitializeData() error {
|
||||
@@ -354,13 +354,13 @@ func (policy *SPolicy) ValidateUpdateData(ctx context.Context, userCred mcclient
|
||||
case api.TAG_UPDATE_POLICY_REMOVE:
|
||||
input.DomainTags = policy.DomainTags.Remove(input.DomainTags...)
|
||||
input.ProjectTags = policy.ProjectTags.Remove(input.ProjectTags...)
|
||||
input.ObjectTags = policy.ResourceTags.Remove(input.ObjectTags...)
|
||||
input.ObjectTags = policy.ObjectTags.Remove(input.ObjectTags...)
|
||||
case api.TAG_UPDATE_POLICY_REPLACE:
|
||||
// do nothing
|
||||
default:
|
||||
input.DomainTags = policy.DomainTags.Append(input.DomainTags...)
|
||||
input.ProjectTags = policy.ProjectTags.Append(input.ProjectTags...)
|
||||
input.ObjectTags = policy.ResourceTags.Append(input.ObjectTags...)
|
||||
input.ObjectTags = policy.ObjectTags.Append(input.ObjectTags...)
|
||||
}
|
||||
|
||||
if input.Blob != nil {
|
||||
@@ -403,7 +403,6 @@ func (policy *SPolicy) PostCreate(ctx context.Context, userCred mcclient.TokenCr
|
||||
if err != nil {
|
||||
log.Errorf("CancelPendingUsage fail %s", err)
|
||||
}
|
||||
|
||||
// policyman.PolicyManager.SyncOnce()
|
||||
}
|
||||
|
||||
@@ -598,7 +597,7 @@ func (policy *SPolicy) GetSharedDomains() []string {
|
||||
}
|
||||
|
||||
func (policy *SPolicy) getPolicy() (*rbacutils.SPolicy, error) {
|
||||
pc, err := rbacutils.DecodePolicyData(policy.DomainTags, policy.ProjectTags, policy.ResourceTags, policy.Blob)
|
||||
pc, err := rbacutils.DecodePolicyData(policy.DomainTags, policy.ProjectTags, policy.ObjectTags, policy.Blob)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Decode")
|
||||
}
|
||||
|
||||
@@ -221,6 +221,12 @@ func (manager *SProjectManager) ListItemFilter(
|
||||
return nil, errors.Wrap(err, "SIdentityBaseResourceManager.ListItemFilter")
|
||||
}
|
||||
|
||||
if len(query.PolicyProjectTags) > 0 {
|
||||
// aplly policy imposed project tag filters
|
||||
subq := db.ObjIdQueryWithTags("project", query.PolicyProjectTags).SubQuery()
|
||||
q = q.In("id", subq)
|
||||
}
|
||||
|
||||
userStr := query.UserId
|
||||
if len(userStr) > 0 {
|
||||
userObj, err := UserManager.FetchById(userStr)
|
||||
|
||||
@@ -20,14 +20,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/tagutils"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/gotypes"
|
||||
"yunion.io/x/pkg/util/reflectutils"
|
||||
|
||||
dbapi "yunion.io/x/onecloud/pkg/apis/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/util/tagutils"
|
||||
)
|
||||
|
||||
// Int returns a pointer to int type with the same value as the argument. This
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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 tagutils // import "yunion.io/x/onecloud/pkg/util/tagutils"
|
||||
+25
-45
@@ -17,6 +17,8 @@ package tagutils
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
type TTagSet []STag
|
||||
@@ -76,54 +78,22 @@ func (ts TTagSet) Remove(ele ...STag) TTagSet {
|
||||
return ts
|
||||
}
|
||||
|
||||
func contains(v1, v2 []string) bool {
|
||||
vv1 := stringutils2.SSortedStrings(v1)
|
||||
vv2 := stringutils2.SSortedStrings(v2)
|
||||
return stringutils2.Contains(vv1, vv2)
|
||||
}
|
||||
|
||||
func (a TTagSet) Contains(b TTagSet) bool {
|
||||
aNoB, _, _ := Split(a, b)
|
||||
if len(aNoB) == 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
mapA := Tagset2Map(a)
|
||||
mapB := Tagset2Map(b)
|
||||
|
||||
func (a TTagSet) Equals(b TTagSet) bool {
|
||||
aNoB, _, bNoA := Split(a, b)
|
||||
if len(aNoB) == 0 && len(bNoA) == 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func Split(a, b TTagSet) (aNoB TTagSet, aAndB TTagSet, bNoA TTagSet) {
|
||||
a_b := make([]STag, 0)
|
||||
b_a := make([]STag, 0)
|
||||
anb := make([]STag, 0)
|
||||
i := 0
|
||||
j := 0
|
||||
for i < len(a) && j < len(b) {
|
||||
switch Compare(a[i], b[j]) {
|
||||
case 0:
|
||||
anb = append(anb, a[i])
|
||||
i += 1
|
||||
j += 1
|
||||
case -1:
|
||||
a_b = append(a_b, a[i])
|
||||
i += 1
|
||||
case 1:
|
||||
b_a = append(b_a, b[j])
|
||||
j += 1
|
||||
for k, v := range mapA {
|
||||
if vs, ok := mapB[k]; !ok || !contains(v, vs) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if i < len(a) {
|
||||
a_b = append(a_b, a[i:]...)
|
||||
}
|
||||
if j < len(b) {
|
||||
b_a = append(b_a, b[j:]...)
|
||||
}
|
||||
aNoB = a_b
|
||||
aAndB = anb
|
||||
bNoA = b_a
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
func Map2Tagset(meta map[string]string) TTagSet {
|
||||
@@ -144,7 +114,17 @@ func Tagset2Map(oTags TTagSet) map[string][]string {
|
||||
tags[tag.Key] = []string{}
|
||||
}
|
||||
if len(tag.Value) > 0 && !utils.IsInStringArray(tag.Value, tags[tag.Key]) {
|
||||
tags[tag.Key] = append(tags[tag.Key], tag.Value)
|
||||
tags[tag.Key] = stringutils2.SSortedStrings(tags[tag.Key]).Append(tag.Value)
|
||||
}
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
func Tagset2MapString(oTags TTagSet) map[string]string {
|
||||
tags := map[string]string{}
|
||||
for _, tag := range oTags {
|
||||
if _, ok := tags[tag.Key]; !ok {
|
||||
tags[tag.Key] = tag.Value
|
||||
}
|
||||
}
|
||||
return tags
|
||||
|
||||
@@ -46,6 +46,29 @@ func TestTTagSet_Contains(t *testing.T) {
|
||||
},
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
t1: TTagSet{
|
||||
STag{
|
||||
Key: "project",
|
||||
Value: "a",
|
||||
},
|
||||
STag{
|
||||
Key: "project",
|
||||
Value: "b",
|
||||
},
|
||||
},
|
||||
t2: TTagSet{
|
||||
STag{
|
||||
Key: "project",
|
||||
Value: "a",
|
||||
},
|
||||
STag{
|
||||
Key: "env",
|
||||
Value: "c",
|
||||
},
|
||||
},
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
t1: TTagSet{
|
||||
STag{
|
||||
|
||||
@@ -85,12 +85,24 @@ func (tsl TTagSetList) String() string {
|
||||
return "[" + strings.Join(tss, ",") + "]"
|
||||
}
|
||||
|
||||
func (tsl TTagSetList) Flattern() TTagSet {
|
||||
ret := TTagSet{}
|
||||
for _, ts := range tsl {
|
||||
for k, v := range ts {
|
||||
ret[k] = v
|
||||
}
|
||||
func (a TTagSetList) Len() int { return len(a) }
|
||||
func (a TTagSetList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a TTagSetList) Less(i, j int) bool {
|
||||
if len(a[i]) < len(a[j]) {
|
||||
return true
|
||||
} else if len(a[i]) > len(a[j]) {
|
||||
return false
|
||||
}
|
||||
return ret
|
||||
if a[i].Contains(a[j]) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (tsl TTagSetList) Flattern() TTagSet {
|
||||
if len(tsl) == 0 {
|
||||
return TTagSet{}
|
||||
}
|
||||
sort.Sort(tsl)
|
||||
return tsl[len(tsl)-1]
|
||||
}
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
|
||||
package tagutils
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
)
|
||||
|
||||
func TestTTagSetList_Contains(t *testing.T) {
|
||||
cases := []struct {
|
||||
@@ -243,3 +248,47 @@ func TestTTagSetList_Append(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTTagSetList_Flattern(t *testing.T) {
|
||||
cases := []struct {
|
||||
tsl TTagSetList
|
||||
want TTagSet
|
||||
}{
|
||||
{
|
||||
tsl: TTagSetList{
|
||||
TTagSet{
|
||||
STag{
|
||||
Key: "project",
|
||||
Value: "a",
|
||||
},
|
||||
STag{
|
||||
Key: "env",
|
||||
Value: "product",
|
||||
},
|
||||
},
|
||||
TTagSet{
|
||||
STag{
|
||||
Key: "project",
|
||||
Value: "b",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: TTagSet{
|
||||
STag{
|
||||
Key: "project",
|
||||
Value: "a",
|
||||
},
|
||||
STag{
|
||||
Key: "env",
|
||||
Value: "product",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := c.tsl.Flattern()
|
||||
if !reflect.DeepEqual(got, c.want) {
|
||||
t.Errorf("want %s got %s", jsonutils.Marshal(c.want), jsonutils.Marshal(got))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user