diff --git a/cmd/climc/shell/groups.go b/cmd/climc/shell/groups.go index 11058249f3..b0b5621777 100644 --- a/cmd/climc/shell/groups.go +++ b/cmd/climc/shell/groups.go @@ -19,41 +19,18 @@ import ( "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/mcclient/options" ) func init() { type GroupListOptions struct { - Admin bool `help:"admin mode"` - Name string `help:"Name of the groups to filter"` - Domain string `help:"Domain to filter"` - Limit int64 `help:"Items per page" default:"20"` - Offset int64 `help:"Offset"` - Search string `help:"search text"` + options.BaseListOptions + Name string `help:"Filter by name"` } R(&GroupListOptions{}, "group-list", "List groups", func(s *mcclient.ClientSession, args *GroupListOptions) error { - params := jsonutils.NewDict() - if len(args.Name) > 0 { - params.Add(jsonutils.NewString(args.Name), "name") - } - if len(args.Domain) > 0 { - domainId, e := modules.Domains.GetId(s, args.Domain, nil) - if e != nil { - return e - } - params.Add(jsonutils.NewString(domainId), "domain_id") - params.Add(jsonutils.JSONTrue, "admin") - } - if args.Admin { - params.Add(jsonutils.JSONTrue, "admin") - } - if args.Limit > 0 { - params.Add(jsonutils.NewInt(args.Limit), "limit") - } - if args.Offset > 0 { - params.Add(jsonutils.NewInt(args.Offset), "offset") - } - if len(args.Search) > 0 { - params.Add(jsonutils.NewString(args.Search), "name__icontains") + params, err := options.ListStructToParams(args) + if err != nil { + return err } result, err := modules.Groups.List(s, params) if err != nil { diff --git a/cmd/climc/shell/policies.go b/cmd/climc/shell/policies.go index 1462fe9d06..5ab0f5336d 100644 --- a/cmd/climc/shell/policies.go +++ b/cmd/climc/shell/policies.go @@ -40,6 +40,7 @@ func init() { Type string `help:"filter by type"` Format string `help:"policy format, default to yaml" default:"yaml" choices:"yaml|json"` Admin bool `help:"admin mode"` + Scope string `help:""` } R(&PolicyListOptions{}, "policy-list", "List all policies", func(s *mcclient.ClientSession, args *PolicyListOptions) error { params := jsonutils.NewDict() @@ -136,6 +137,27 @@ func init() { return nil }) + type PolicyPerformOptions struct { + ID string `help:"ID of policy to update"` + } + R(&PolicyPerformOptions{}, "policy-public", "Mark a policy public", func(s *mcclient.ClientSession, args *PolicyPerformOptions) error { + result, err := modules.Policies.PerformAction(s, args.ID, "public", nil) + if err != nil { + return err + } + printObject(result) + return nil + }) + + R(&PolicyPerformOptions{}, "policy-private", "Mark a policy private", func(s *mcclient.ClientSession, args *PolicyPerformOptions) error { + result, err := modules.Policies.PerformAction(s, args.ID, "private", nil) + if err != nil { + return err + } + printObject(result) + return nil + }) + type PolicyDeleteOptions struct { ID string `help:"ID of policy"` } diff --git a/cmd/climc/shell/usages.go b/cmd/climc/shell/usages.go index 94fa9c7ace..099e5a5056 100644 --- a/cmd/climc/shell/usages.go +++ b/cmd/climc/shell/usages.go @@ -26,7 +26,8 @@ type GeneralUsageOptions struct { Provider []string `help:"Provider" choices:"VMware|Aliyun|Azure|Aws|Qcloud|Huawei|OpenStack|Ucloud|ZStack"` Project string `help:"show usage of specified project"` Domain string `help:"show usage of specified domain"` - CloudEnv string `help:"show usage of specified cloudenv, e.g public_cloud/private_cloud/on_premise" choices:"public|private|onpremise"` + CloudEnv string `help:"show usage of specified cloudenv, e.g. public_cloud/private_cloud/on_premise" choices:"public|private|onpremise"` + Scope string `help:"show usage of specified privilege scope, e.g. system/domain/project" choices:"system|domain|project"` } func fetchHostTypeOptions(args *GeneralUsageOptions) *jsonutils.JSONDict { @@ -51,6 +52,9 @@ func init() { } else if args.Domain != "" { params.Add(jsonutils.NewString(args.Domain), "domain") } + if len(args.Scope) > 0 { + params.Add(jsonutils.NewString(args.Scope), "scope") + } result, err := modules.Usages.GetGeneralUsage(s, params) if err != nil { return err diff --git a/cmd/climc/shell/users.go b/cmd/climc/shell/users.go index 80c16114f6..553e142e8a 100644 --- a/cmd/climc/shell/users.go +++ b/cmd/climc/shell/users.go @@ -19,56 +19,18 @@ import ( "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/mcclient/options" ) func init() { type UserListOptions struct { - System bool `help:"system mode"` - Admin bool `help:"admin mode"` - Domain string `help:"Filter by domain"` - Name string `help:"Filter by name"` - Limit int64 `help:"Limit, default 0, i.e. no limit"` - Offset int64 `help:"Offset, default 0, i.e. no offset"` - Search string `help:"Search by name"` - DefaultProject string `help:"Filter by default_project_id"` - NoDefaultProject bool `help:"Filter users without valid default_project_id"` + options.BaseListOptions + Name string `help:"Filter by name"` } R(&UserListOptions{}, "user-list", "List users", func(s *mcclient.ClientSession, args *UserListOptions) error { - params := jsonutils.NewDict() - if len(args.Domain) > 0 { - domainId, err := modules.Domains.GetId(s, args.Domain, nil) - if err != nil { - return err - } - params.Add(jsonutils.NewString(domainId), "domain_id") - params.Add(jsonutils.JSONTrue, "admin") - } - if args.Admin { - params.Add(jsonutils.JSONTrue, "admin") - } - if args.System { - params.Add(jsonutils.JSONTrue, "system") - } - if len(args.Name) > 0 { - params.Add(jsonutils.NewString(args.Name), "name") - } - if len(args.Search) > 0 { - params.Add(jsonutils.NewString(args.Search), "name__icontains") - } - if args.Limit > 0 { - params.Add(jsonutils.NewInt(args.Limit), "limit") - } - if args.Offset > 0 { - params.Add(jsonutils.NewInt(args.Offset), "offset") - } - if len(args.DefaultProject) > 0 { - projId, err := modules.Projects.GetId(s, args.DefaultProject, nil) - if err != nil { - return err - } - params.Add(jsonutils.NewString(projId), "default_project_id") - } else if args.NoDefaultProject { - params.Add(jsonutils.NewString(""), "default_project_id__iempty") + params, err := options.ListStructToParams(args) + if err != nil { + return err } result, err := modules.UsersV3.List(s, params) if err != nil { diff --git a/pkg/cloudcommon/db/domain.go b/pkg/cloudcommon/db/domain.go index 1b91da0f1f..30745c5372 100644 --- a/pkg/cloudcommon/db/domain.go +++ b/pkg/cloudcommon/db/domain.go @@ -36,8 +36,13 @@ func (manager *SDomainizedResourceBaseManager) ResourceScope() rbacutils.TRbacSc } func (manager *SDomainizedResourceBaseManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery { - if owner != nil && len(owner.GetProjectDomainId()) > 0 { - q = q.Equals("domain_id", owner.GetProjectDomainId()) + if owner != nil { + switch scope { + case rbacutils.ScopeDomain: + if len(owner.GetProjectDomainId()) > 0 { + q = q.Equals("domain_id", owner.GetProjectDomainId()) + } + } } return q } diff --git a/pkg/cloudcommon/db/fetch.go b/pkg/cloudcommon/db/fetch.go index f0a83cf652..188df0e1d5 100644 --- a/pkg/cloudcommon/db/fetch.go +++ b/pkg/cloudcommon/db/fetch.go @@ -255,7 +255,7 @@ func FetchProjectInfo(ctx context.Context, data jsonutils.JSONObject) (mcclient. } return &ownerId, nil } - return nil, nil + return FetchDomainInfo(ctx, data) } func FetchDomainInfo(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) { @@ -284,6 +284,10 @@ func (m *sUsageManager) ResourceScope() rbacutils.TRbacScope { return rbacutils.ScopeProject } +func (m *sUsageManager) FetchOwnerId(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) { + return FetchProjectInfo(ctx, data) +} + func FetchUsageOwnerScope(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) (mcclient.IIdentityProvider, rbacutils.TRbacScope, error) { return FetchQueryOwnerScope(ctx, userCred, data, &sUsageManager{}, policy.PolicyActionGet) } @@ -291,6 +295,7 @@ func FetchUsageOwnerScope(ctx context.Context, userCred mcclient.TokenCredential type IScopedResourceManager interface { KeywordPlural() string ResourceScope() rbacutils.TRbacScope + FetchOwnerId(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) } func FetchQueryOwnerScope(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject, manager IScopedResourceManager, action string) (mcclient.IIdentityProvider, rbacutils.TRbacScope, error) { @@ -315,65 +320,43 @@ func FetchQueryOwnerScope(ctx context.Context, userCred mcclient.TokenCredential } } - ownerId, err := FetchProjectInfo(ctx, data) + // var ownerId mcclient.IIdentityProvider + // var err error + + ownerId, err := manager.FetchOwnerId(ctx, data) if err != nil { return nil, queryScope, err } if ownerId != nil { - if resScope == rbacutils.ScopeProject { - queryScope = rbacutils.ScopeProject - if ownerId.GetProjectId() == userCred.GetProjectId() { - requireScope = rbacutils.ScopeProject - } else if ownerId.GetProjectDomainId() == userCred.GetProjectDomainId() { - requireScope = rbacutils.ScopeDomain - } else { - requireScope = rbacutils.ScopeSystem - } - } else if resScope == rbacutils.ScopeDomain { - queryScope = rbacutils.ScopeDomain - if ownerId.GetProjectDomainId() == userCred.GetProjectDomainId() { - requireScope = rbacutils.ScopeDomain - } else { - requireScope = rbacutils.ScopeSystem - } - } else { - return nil, queryScope, httperrors.NewInputParameterError("query scope out of resource scope") - } - } else { - ownerId, err = FetchUserInfo(ctx, data) - if err != nil { - return nil, queryScope, err - } - if ownerId != nil { - if resScope == rbacutils.ScopeUser { - queryScope = rbacutils.ScopeUser - if ownerId.GetUserId() == userCred.GetUserId() { - requireScope = rbacutils.ScopeUser + switch resScope { + case rbacutils.ScopeProject, rbacutils.ScopeDomain: + if len(ownerId.GetProjectId()) > 0 { + queryScope = rbacutils.ScopeProject + if ownerId.GetProjectId() == userCred.GetProjectId() { + requireScope = rbacutils.ScopeProject + } else if ownerId.GetProjectDomainId() == userCred.GetProjectDomainId() { + requireScope = rbacutils.ScopeDomain } else { requireScope = rbacutils.ScopeSystem } - } else { - return nil, queryScope, httperrors.NewInputParameterError("query scope out of resource scope") - } - } else { - ownerId, err = FetchDomainInfo(ctx, data) - if err != nil { - return nil, queryScope, err - } - if ownerId != nil { - if resScope == rbacutils.ScopeDomain { - queryScope = rbacutils.ScopeDomain - if ownerId.GetProjectDomainId() == userCred.GetProjectDomainId() { - requireScope = rbacutils.ScopeDomain - } else { - requireScope = rbacutils.ScopeSystem - } + } else if len(ownerId.GetProjectDomainId()) > 0 { + queryScope = rbacutils.ScopeDomain + if ownerId.GetProjectDomainId() == userCred.GetProjectDomainId() { + requireScope = rbacutils.ScopeDomain } else { - return nil, queryScope, httperrors.NewInputParameterError("query scope out of resource scope") + requireScope = rbacutils.ScopeSystem } } + case rbacutils.ScopeUser: + queryScope = rbacutils.ScopeUser + if ownerId.GetUserId() == userCred.GetUserId() { + requireScope = rbacutils.ScopeUser + } else { + requireScope = rbacutils.ScopeSystem + } } } + if ownerId == nil { ownerId = userCred reqScopeStr, _ := data.GetString("scope") @@ -391,7 +374,7 @@ func FetchQueryOwnerScope(ctx context.Context, userCred mcclient.TokenCredential requireScope = queryScope } if requireScope.HigherThan(allowScope) { - return nil, scope, httperrors.NewForbiddenError("not enough privilleges") + return nil, scope, httperrors.NewForbiddenError(fmt.Sprintf("not enough privilleges(require:%s,allow:%s:query:%s)", requireScope, allowScope, queryScope)) } return ownerId, queryScope, nil } diff --git a/pkg/cloudcommon/db/models.go b/pkg/cloudcommon/db/models.go index 510965604f..509b4ba9c5 100644 --- a/pkg/cloudcommon/db/models.go +++ b/pkg/cloudcommon/db/models.go @@ -35,11 +35,11 @@ func RegisterModelManager(modelMan IModelManager) { func mustCheckModelManager(modelMan IModelManager) { allowedTags := map[string][]string{ - "create": {"required", "optional", "admin_required", "admin_optional"}, - "search": {"user", "admin"}, - "get": {"user", "admin"}, - "list": {"user", "admin"}, - "update": {"user", "admin"}, + "create": {"required", "optional", "domain_required", "domain_optional", "admin_required", "admin_optional"}, + "search": {"user", "domain", "admin"}, + "get": {"user", "domain", "admin"}, + "list": {"user", "domain", "admin"}, + "update": {"user", "domain", "admin"}, } for _, col := range modelMan.TableSpec().Columns() { tags := col.Tags() diff --git a/pkg/cloudcommon/db/project.go b/pkg/cloudcommon/db/project.go index e6ed4d872f..de3c2a84dc 100644 --- a/pkg/cloudcommon/db/project.go +++ b/pkg/cloudcommon/db/project.go @@ -64,9 +64,5 @@ func (manager *SProjectizedResourceBaseManager) ResourceScope() rbacutils.TRbacS } func (manager *SProjectizedResourceBaseManager) FetchOwnerId(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) { - ownerId, err := FetchProjectInfo(ctx, data) - if err == nil && ownerId == nil { - ownerId, err = FetchDomainInfo(ctx, data) - } - return nil, nil + return FetchProjectInfo(ctx, data) } diff --git a/pkg/cloudcommon/db/quotas/quotas.go b/pkg/cloudcommon/db/quotas/quotas.go index d930f97b97..ef4472b2f6 100644 --- a/pkg/cloudcommon/db/quotas/quotas.go +++ b/pkg/cloudcommon/db/quotas/quotas.go @@ -21,6 +21,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/rbacutils" @@ -56,6 +57,10 @@ func (manager *SQuotaManager) ResourceScope() rbacutils.TRbacScope { return rbacutils.ScopeProject } +func (manager *SQuotaManager) FetchOwnerId(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) { + return db.FetchProjectInfo(ctx, data) +} + func (manager *SQuotaManager) Keyword() string { return manager.keyword } diff --git a/pkg/cloudcommon/db/rbac.go b/pkg/cloudcommon/db/rbac.go index cf43444d5f..857dd8ae86 100644 --- a/pkg/cloudcommon/db/rbac.go +++ b/pkg/cloudcommon/db/rbac.go @@ -21,10 +21,6 @@ import ( "yunion.io/x/onecloud/pkg/util/rbacutils" ) -func getListRbacAllowedScope(manager IModelManager, userCred mcclient.TokenCredential) rbacutils.TRbacScope { - return policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionList) -} - func isObjectRbacAllowed(model IModel, userCred mcclient.TokenCredential, action string, extra ...string) bool { manager := model.GetModelManager() objOwnerId := model.GetOwnerId() @@ -46,6 +42,12 @@ func isObjectRbacAllowed(model IModel, userCred mcclient.TokenCredential, action } else { requireScope = rbacutils.ScopeSystem } + case rbacutils.ScopeUser: + if ownerId != nil && ownerId.GetUserId() == objOwnerId.GetUserId() { + requireScope = rbacutils.ScopeUser + } else { + requireScope = rbacutils.ScopeSystem + } default: // objOwnerId should not be nil if ownerId != nil && (ownerId.GetProjectId() == objOwnerId.GetProjectId() || (model.IsSharable() && action == policy.PolicyActionGet)) { diff --git a/pkg/cloudcommon/db/sharablevirtual.go b/pkg/cloudcommon/db/sharablevirtual.go index a73485b0b4..3e515a68e6 100644 --- a/pkg/cloudcommon/db/sharablevirtual.go +++ b/pkg/cloudcommon/db/sharablevirtual.go @@ -22,6 +22,7 @@ import ( "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" ) @@ -29,8 +30,8 @@ import ( type SSharableVirtualResourceBase struct { SVirtualResourceBase - IsPublic bool `default:"false" nullable:"false" index:"true" create:"admin_optional" list:"user" update:"admin"` - PublicScope string `width:"16" charset:"ascii" nullable:"false" default:"system" create:"admin_optional" list:"user" update:"admin"` + IsPublic bool `default:"false" nullable:"false" create:"domain_optional" list:"user"` + PublicScope string `width:"16" charset:"ascii" nullable:"false" default:"system" create:"domain_optional" list:"user"` } type SSharableVirtualResourceBaseManager struct { @@ -90,13 +91,12 @@ func (model *SSharableVirtualResourceBase) AllowPerformPublic(ctx context.Contex return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "public") } -func (model *SSharableVirtualResourceBase) AllowPerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { - return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "private") -} - func (model *SSharableVirtualResourceBase) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + scope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "public") + if !scope.HigherThan(rbacutils.ScopeProject) { + return nil, httperrors.NewForbiddenError("not enough privilege") + } if !model.IsPublic { - scope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.GetModelManager().KeywordPlural(), "public") diff, err := Update(model, func() error { model.IsPublic = true model.PublicScope = string(scope) @@ -110,7 +110,15 @@ func (model *SSharableVirtualResourceBase) PerformPublic(ctx context.Context, us return nil, nil } +func (model *SSharableVirtualResourceBase) AllowPerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "private") +} + func (model *SSharableVirtualResourceBase) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + scope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "private") + if !scope.HigherThan(rbacutils.ScopeProject) { + return nil, httperrors.NewForbiddenError("not enough privilege") + } if model.IsPublic { diff, err := Update(model, func() error { model.IsPublic = false diff --git a/pkg/cloudcommon/db/virtualresource.go b/pkg/cloudcommon/db/virtualresource.go index 4dabacb685..1b964a52c6 100644 --- a/pkg/cloudcommon/db/virtualresource.go +++ b/pkg/cloudcommon/db/virtualresource.go @@ -103,7 +103,10 @@ func (manager *SVirtualResourceBaseManager) FilterBySystemAttributes(q *sqlchemy q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("is_system")), sqlchemy.IsFalse(q.Field("is_system")))) } - pendingDelete, _ := query.GetString("pending_delete") + var pendingDelete string + if query != nil { + pendingDelete, _ = query.GetString("pending_delete") + } pendingDeleteLower := strings.ToLower(pendingDelete) if pendingDeleteLower == "all" || pendingDeleteLower == "any" || utils.ToBool(pendingDeleteLower) { var isAllow bool diff --git a/pkg/cloudcommon/policy/policy.go b/pkg/cloudcommon/policy/policy.go index efe9f6b5f6..34cbed4414 100644 --- a/pkg/cloudcommon/policy/policy.go +++ b/pkg/cloudcommon/policy/policy.go @@ -88,6 +88,8 @@ func parseJsonPolicy(obj jsonutils.JSONObject) (string, *rbacutils.SRbacPolicy, return "", nil, errors.Wrap(err, "missing domain_id") } + isPublic := jsonutils.QueryBoolean(obj, "is_public", false) + blob, err := obj.Get("policy") if err != nil { log.Errorf("get blob error %s", err) @@ -102,6 +104,7 @@ func parseJsonPolicy(obj jsonutils.JSONObject) (string, *rbacutils.SRbacPolicy, } policy.DomainId = domainId + policy.IsPublic = isPublic return typeStr, &policy, nil } @@ -117,6 +120,7 @@ func remotePolicyFetcher() (map[rbacutils.TRbacScope]map[string]*rbacutils.SRbac params.Add(jsonutils.NewInt(2048), "limit") params.Add(jsonutils.NewInt(int64(offset)), "offset") params.Add(jsonutils.JSONTrue, "admin") + params.Add(jsonutils.JSONTrue, "enabled") result, err := modules.Policies.List(s, params) if err != nil { return nil, errors.Wrap(err, "modules.Policies.List") diff --git a/pkg/compute/models/cloudaccounts.go b/pkg/compute/models/cloudaccounts.go index b6d2b1e726..704cb73d9f 100644 --- a/pkg/compute/models/cloudaccounts.go +++ b/pkg/compute/models/cloudaccounts.go @@ -67,37 +67,37 @@ type SCloudaccount struct { db.SDomainizedResourceBase SSyncableBaseResource - LastAutoSync time.Time `list:"admin"` + LastAutoSync time.Time `list:"domain"` - AccessUrl string `width:"64" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` + AccessUrl string `width:"64" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"domain_optional"` - Account string `width:"128" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"` // Column(VARCHAR(64, charset='ascii'), nullable=False) - Secret string `width:"256" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"` // Column(VARCHAR(256, charset='ascii'), nullable=False) + Account string `width:"128" charset:"ascii" nullable:"false" list:"domain" create:"domain_required"` // Column(VARCHAR(64, charset='ascii'), nullable=False) + Secret string `width:"256" charset:"ascii" nullable:"false" list:"domain" create:"domain_required"` // Column(VARCHAR(256, charset='ascii'), nullable=False) - // BalanceKey string `width:"256" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` + // BalanceKey string `width:"256" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"domain_optional"` IsPublicCloud *bool `nullable:"false" get:"user" create:"optional" list:"user" default:"true"` IsOnPremise bool `nullable:"false" get:"user" create:"optional" list:"user" default:"false"` - Provider string `width:"64" charset:"ascii" list:"admin" create:"admin_required"` + Provider string `width:"64" charset:"ascii" list:"domain" create:"domain_required"` - EnableAutoSync bool `default:"false" create:"admin_optional" list:"admin"` - SyncIntervalSeconds int `create:"admin_optional" list:"admin" update:"admin"` + EnableAutoSync bool `default:"false" create:"domain_optional" list:"domain"` + SyncIntervalSeconds int `create:"domain_optional" list:"domain" update:"domain"` - Balance float64 `list:"admin"` - ProbeAt time.Time `list:"admin"` - HealthStatus string `width:"16" charset:"ascii" default:"normal" nullable:"false" list:"admin"` + Balance float64 `list:"domain"` + ProbeAt time.Time `list:"domain"` + HealthStatus string `width:"16" charset:"ascii" default:"normal" nullable:"false" list:"domain"` - ErrorCount int `list:"admin"` + ErrorCount int `list:"domain"` - AutoCreateProject bool `list:"admin" create:"admin_optional"` + AutoCreateProject bool `list:"domain" create:"domain_optional"` - Version string `width:"32" charset:"ascii" nullable:"true" list:"admin"` // Column(VARCHAR(32, charset='ascii'), nullable=True) - Sysinfo jsonutils.JSONObject `get:"admin"` // Column(JSONEncodedDict, nullable=True) + Version string `width:"32" charset:"ascii" nullable:"true" list:"domain"` // Column(VARCHAR(32, charset='ascii'), nullable=True) + Sysinfo jsonutils.JSONObject `get:"domain"` // Column(JSONEncodedDict, nullable=True) - Brand string `width:"64" charset:"utf8" nullable:"true" list:"admin" create:"optional"` + Brand string `width:"64" charset:"utf8" nullable:"true" list:"domain" create:"optional"` - Options *jsonutils.JSONDict `get:"admin" create:"admin_optional" update:"admin"` + Options *jsonutils.JSONDict `get:"domain" create:"domain_optional" update:"domain"` } func (self *SCloudaccountManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { diff --git a/pkg/compute/models/cloudproviderregions.go b/pkg/compute/models/cloudproviderregions.go index 50739e99f6..14785c65bb 100644 --- a/pkg/compute/models/cloudproviderregions.go +++ b/pkg/compute/models/cloudproviderregions.go @@ -19,15 +19,17 @@ import ( "database/sql" "math/rand" "time" + "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/util/compare" + "yunion.io/x/pkg/util/timeutils" + "yunion.io/x/sqlchemy" + "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" - "yunion.io/x/pkg/util/compare" - "yunion.io/x/pkg/util/timeutils" - "yunion.io/x/sqlchemy" ) type SCloudproviderregionManager struct { @@ -57,15 +59,15 @@ type SCloudproviderregion struct { SSyncableBaseResource - CloudproviderId string `width:"36" charset:"ascii" nullable:"false" list:"admin"` // Column(VARCHAR(36, charset='ascii'), nullable=False) - CloudregionId string `width:"36" charset:"ascii" nullable:"false" list:"admin"` + CloudproviderId string `width:"36" charset:"ascii" nullable:"false" list:"domain"` // Column(VARCHAR(36, charset='ascii'), nullable=False) + CloudregionId string `width:"36" charset:"ascii" nullable:"false" list:"domain"` - Enabled bool `nullable:"false" list:"admin" update:"admin"` + Enabled bool `nullable:"false" list:"domain" update:"domain"` - // SyncIntervalSeconds int `list:"admin"` - SyncResults jsonutils.JSONObject `list:"admin"` + // SyncIntervalSeconds int `list:"domain"` + SyncResults jsonutils.JSONObject `list:"domain"` - LastDeepSyncAt time.Time `list:"admin"` + LastDeepSyncAt time.Time `list:"domain"` } func (manager *SCloudproviderregionManager) GetMasterFieldName() string { diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index b15f2be75d..1535025d30 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -69,23 +69,23 @@ type SCloudprovider struct { SSyncableBaseResource - HealthStatus string `width:"16" charset:"ascii" default:"normal" nullable:"false" list:"admin"` // 云端服务健康状态。例如欠费、项目冻结都属于不健康状态。 + HealthStatus string `width:"16" charset:"ascii" default:"normal" nullable:"false" list:"domain"` // 云端服务健康状态。例如欠费、项目冻结都属于不健康状态。 // Hostname string `width:"64" charset:"ascii" nullable:"true"` // Column(VARCHAR(64, charset='ascii'), nullable=False) // port = Column(Integer, nullable=False) - // Version string `width:"32" charset:"ascii" nullable:"true" list:"admin"` // Column(VARCHAR(32, charset='ascii'), nullable=True) - // Sysinfo jsonutils.JSONObject `get:"admin"` // Column(JSONEncodedDict, nullable=True) + // Version string `width:"32" charset:"ascii" nullable:"true" list:"domain"` // Column(VARCHAR(32, charset='ascii'), nullable=True) + // Sysinfo jsonutils.JSONObject `get:"domain"` // Column(JSONEncodedDict, nullable=True) - AccessUrl string `width:"64" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` - Account string `width:"128" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"` // Column(VARCHAR(64, charset='ascii'), nullable=False) - Secret string `width:"256" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"` // Column(VARCHAR(256, charset='ascii'), nullable=False) + AccessUrl string `width:"64" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"domain_optional"` + Account string `width:"128" charset:"ascii" nullable:"false" list:"domain" create:"domain_required"` // Column(VARCHAR(64, charset='ascii'), nullable=False) + Secret string `width:"256" charset:"ascii" nullable:"false" list:"domain" create:"domain_required"` // Column(VARCHAR(256, charset='ascii'), nullable=False) CloudaccountId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"` - // ProjectId string `name:"tenant_id" width:"128" charset:"ascii" nullable:"true" list:"admin"` + // ProjectId string `name:"tenant_id" width:"128" charset:"ascii" nullable:"true" list:"domain"` - // LastSync time.Time `get:"admin" list:"admin"` // = Column(DateTime, nullable=True) + // LastSync time.Time `get:"domain" list:"domain"` // = Column(DateTime, nullable=True) - Provider string `width:"64" charset:"ascii" list:"admin" create:"admin_required"` + Provider string `width:"64" charset:"ascii" list:"domain" create:"domain_required"` } func (self *SCloudproviderManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index d42499fcd0..5e12a4f756 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -35,9 +35,9 @@ import ( ) type SSyncableBaseResource struct { - SyncStatus string `width:"10" charset:"ascii" default:"idle" list:"admin"` - LastSync time.Time `list:"admin"` // = Column(DateTime, nullable=True) - LastSyncEndAt time.Time `list:"admin"` + SyncStatus string `width:"10" charset:"ascii" default:"idle" list:"domain"` + LastSync time.Time `list:"domain"` // = Column(DateTime, nullable=True) + LastSyncEndAt time.Time `list:"domain"` } func (self *SSyncableBaseResource) CanSync() bool { diff --git a/pkg/compute/models/skus.go b/pkg/compute/models/skus.go index 2967165c4a..526cb88c93 100644 --- a/pkg/compute/models/skus.go +++ b/pkg/compute/models/skus.go @@ -25,11 +25,10 @@ import ( "strings" "time" - "yunion.io/x/pkg/utils" - "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/util/compare" + "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" api "yunion.io/x/onecloud/pkg/apis/compute" diff --git a/pkg/compute/models/snapshotpolicy.go b/pkg/compute/models/snapshotpolicy.go index 8d52937239..d6f7585fb7 100644 --- a/pkg/compute/models/snapshotpolicy.go +++ b/pkg/compute/models/snapshotpolicy.go @@ -23,7 +23,6 @@ import ( "yunion.io/x/pkg/util/compare" "yunion.io/x/pkg/utils" - "yunion.io/x/onecloud/pkg/apis/compute" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" @@ -66,7 +65,7 @@ func init() { } func (manager *SSnapshotPolicyManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - input := &compute.SSnapshotPolicyCreateInput{} + input := &api.SSnapshotPolicyCreateInput{} err := data.Unmarshal(input) if err != nil { return nil, httperrors.NewInputParameterError("Unmarshal input failed %s", err) @@ -114,7 +113,7 @@ func (self *SSnapshotPolicy) StartCreateSnapshotPolicy(ctx context.Context, user } func (self *SSnapshotPolicy) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error { - self.SetStatus(userCred, compute.SNAPSHOT_POLICY_DELETING, "") + self.SetStatus(userCred, api.SNAPSHOT_POLICY_DELETING, "") return self.StartSnapshotPolicyDeleteTask(ctx, userCred, jsonutils.NewDict(), "") } @@ -375,7 +374,7 @@ func (self *SSnapshotPolicy) preCheck( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject, ) ([]string, error) { - if self.Status != compute.SNAPSHOT_POLICY_READY { + if self.Status != api.SNAPSHOT_POLICY_READY { return nil, httperrors.NewInvalidStatusError("Snapshot policy status %s can't do apply", self.Status) } jsonDiskIds, err := data.Get("disks") diff --git a/pkg/keystone/models/domains.go b/pkg/keystone/models/domains.go index c33f46e610..de270cae8e 100644 --- a/pkg/keystone/models/domains.go +++ b/pkg/keystone/models/domains.go @@ -245,11 +245,14 @@ func (domain *SDomain) ValidateDeleteCondition(ctx context.Context) error { return domain.SStandaloneResourceBase.ValidateDeleteCondition(ctx) } -func (domain *SDomain) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { +func (domain *SDomain) ValidateUpdateCondition(ctx context.Context) error { if domain.Id == api.DEFAULT_DOMAIN_ID { - return nil, httperrors.NewForbiddenError("default domain is protected") + return httperrors.NewForbiddenError("default domain is protected") } - return domain.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + if domain.IsReadOnly() { + return httperrors.NewForbiddenError("readonly") + } + return domain.SStandaloneResourceBase.ValidateUpdateCondition(ctx) } /*func (domain *SDomain) isReadOnly() bool { diff --git a/pkg/keystone/models/groups.go b/pkg/keystone/models/groups.go index 396f714235..0d3b0f77ad 100644 --- a/pkg/keystone/models/groups.go +++ b/pkg/keystone/models/groups.go @@ -66,7 +66,7 @@ func init() { type SGroup struct { SIdentityBaseResource - Displayname string `with:"128" charset:"utf8" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` + Displayname string `with:"128" charset:"utf8" nullable:"true" list:"domain" update:"domain" create:"domain_optional"` } func (manager *SGroupManager) GetContextManagers() [][]db.IModelManager { @@ -209,8 +209,11 @@ func (manager *SGroupManager) RegisterExternalGroup(ctx context.Context, idpId s return &group, nil } -func (group *SGroup) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - return group.SIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, data) +func (group *SGroup) ValidateUpdateCondition(ctx context.Context) error { + if group.IsReadOnly() { + return httperrors.NewForbiddenError("readonly") + } + return group.SIdentityBaseResource.ValidateUpdateCondition(ctx) } func (manager *SGroupManager) fetchGroupById(gid string) *SGroup { diff --git a/pkg/keystone/models/identitybase.go b/pkg/keystone/models/identitybase.go index eafec9322b..06c50811e2 100644 --- a/pkg/keystone/models/identitybase.go +++ b/pkg/keystone/models/identitybase.go @@ -77,7 +77,7 @@ func NewEnabledIdentityBaseResourceManager(dt interface{}, tableName string, key type SEnabledIdentityBaseResource struct { SIdentityBaseResource - Enabled tristate.TriState `nullable:"false" default:"true" list:"admin" update:"admin" create:"admin_optional"` + Enabled tristate.TriState `nullable:"false" default:"true" list:"user" update:"domain" create:"domain_optional"` } func (model *SIdentityBaseResource) GetIIdentityModelManager() IIdentityModelManager { @@ -197,23 +197,15 @@ func (model *SIdentityBaseResource) CustomizeCreate(ctx context.Context, userCre return model.SStandaloneResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) } +/* func (self *SIdentityBaseResource) ValidateDeleteCondition(ctx context.Context) error { - // domain := self.GetDomain() - // if self.GetIIdentityModelManager().IsDomainReadonly(domain) { - // return httperrors.NewForbiddenError("readonly domain") - // } return self.SStandaloneResourceBase.ValidateDeleteCondition(ctx) } func (self *SIdentityBaseResource) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - // if data.Contains("name") { - // domain := self.GetDomain() - // if self.GetIIdentityModelManager().IsDomainReadonly(domain) { - // return nil, httperrors.NewForbiddenError("cannot update name in readonly domain") - // } - // } return self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) } +*/ func (self *SEnabledIdentityBaseResource) ValidateDeleteCondition(ctx context.Context) error { if self.Enabled.IsTrue() { diff --git a/pkg/keystone/models/policies.go b/pkg/keystone/models/policies.go index 8dace1a1ba..4864bd0343 100644 --- a/pkg/keystone/models/policies.go +++ b/pkg/keystone/models/policies.go @@ -19,11 +19,14 @@ import ( "database/sql" "yunion.io/x/jsonutils" + "yunion.io/x/sqlchemy" + "yunion.io/x/onecloud/pkg/cloudcommon/consts" "yunion.io/x/onecloud/pkg/cloudcommon/db" policyman "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" ) type SPolicyManager struct { @@ -58,8 +61,10 @@ func init() { type SPolicy struct { SEnabledIdentityBaseResource - Type string `width:"255" charset:"utf8" nullable:"false" list:"user" update:"admin"` - Blob jsonutils.JSONObject `nullable:"false" list:"user" update:"admin"` + Type string `width:"255" charset:"utf8" nullable:"false" list:"user" update:"domain"` + Blob jsonutils.JSONObject `nullable:"false" list:"user" update:"domain"` + + IsPublic bool `default:"false" nullable:"false" list:"user"` } func (manager *SPolicyManager) InitializeData() error { @@ -100,9 +105,48 @@ func (manager *SPolicyManager) ValidateCreateData(ctx context.Context, userCred if !data.Contains("name") { data.Set("name", jsonutils.NewString(typeStr)) } + blobJson, err := data.Get("blob") + if err != nil { + return nil, httperrors.NewInputParameterError("invalid policy data") + } + policy := rbacutils.SRbacPolicy{} + err = policy.Decode(blobJson) + if err != nil { + return nil, httperrors.NewInputParameterError("fail to decode policy data") + } + if policy.IsSystemWidePolicy() && policyman.PolicyManager.Allow(rbacutils.ScopeSystem, userCred, consts.GetServiceType(), manager.KeywordPlural(), policyman.PolicyActionCreate) == rbacutils.Deny { + return nil, httperrors.NewNotSufficientPrivilegeError("not allow to create system-wide policy") + } return manager.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, data) } +func (policy *SPolicy) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + if data.Contains("blob") { + blobJson, err := data.Get("blob") + if err != nil { + return nil, httperrors.NewInputParameterError("invalid policy data") + } + p := rbacutils.SRbacPolicy{} + err = p.Decode(blobJson) + if err != nil { + return nil, httperrors.NewInputParameterError("fail to decode policy data") + } + if p.IsSystemWidePolicy() && policyman.PolicyManager.Allow(rbacutils.ScopeSystem, userCred, consts.GetServiceType(), policy.GetModelManager().KeywordPlural(), policyman.PolicyActionUpdate) == rbacutils.Deny { + return nil, httperrors.NewNotSufficientPrivilegeError("not allow to update system-wide policy") + } + } + if data.Contains("type") { + typeStr, _ := data.GetString("type") + if len(typeStr) == 0 { + return nil, httperrors.NewInputParameterError("empty name") + } + if len(typeStr) > 0 { + data.Set("name", jsonutils.NewString(typeStr)) + } + } + return policy.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) +} + func (policy *SPolicy) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { policy.SStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data) policyman.PolicyManager.SyncOnce() @@ -117,3 +161,75 @@ func (policy *SPolicy) PostDelete(ctx context.Context, userCred mcclient.TokenCr policy.SStandaloneResourceBase.PostDelete(ctx, userCred) policyman.PolicyManager.SyncOnce() } + +func (policy *SPolicy) AllowPerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAllowPerform(rbacutils.ScopeSystem, userCred, policy, "public") +} + +func (policy *SPolicy) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if policy.IsPublic { + return nil, nil + } + + scope := policyman.PolicyManager.AllowScope(userCred, consts.GetServiceType(), policy.GetModelManager().KeywordPlural(), policyman.PolicyActionPerform, "public") + if scope != rbacutils.ScopeSystem { + return nil, httperrors.NewForbiddenError("not enough privilege") + } + + p := rbacutils.SRbacPolicy{} + err := p.Decode(policy.Blob) + if err != nil { + return nil, httperrors.NewInputParameterError("fail to decode policy data") + } + if !p.IsSystemWidePolicy() { + return nil, httperrors.NewInvalidStatusError("only system-wide policy (no roles and projects constraints) is sharable") + } + + diff, err := db.Update(policy, func() error { + policy.IsPublic = true + return nil + }) + if err == nil { + db.OpsLog.LogEvent(policy, db.ACT_UPDATE, diff, userCred) + } + return nil, err +} + +func (policy *SPolicy) AllowPerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAllowPerform(rbacutils.ScopeSystem, userCred, policy, "private") +} + +func (policy *SPolicy) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if !policy.IsPublic { + return nil, nil + } + + scope := policyman.PolicyManager.AllowScope(userCred, consts.GetServiceType(), policy.GetModelManager().KeywordPlural(), policyman.PolicyActionPerform, "private") + if scope != rbacutils.ScopeSystem { + return nil, httperrors.NewForbiddenError("not enough privilege") + } + + diff, err := db.Update(policy, func() error { + policy.IsPublic = false + return nil + }) + if err == nil { + db.OpsLog.LogEvent(policy, db.ACT_UPDATE, diff, userCred) + } + return nil, err +} + +func (manager *SPolicyManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery { + if owner != nil { + switch scope { + case rbacutils.ScopeDomain: + if len(owner.GetProjectDomainId()) > 0 { + q = q.Filter(sqlchemy.OR( + sqlchemy.Equals(q.Field("domain_id"), owner.GetProjectDomainId()), + sqlchemy.IsTrue(q.Field("is_public")), + )) + } + } + } + return q +} diff --git a/pkg/keystone/models/projects.go b/pkg/keystone/models/projects.go index 79edce4aa9..aa210fd1c5 100644 --- a/pkg/keystone/models/projects.go +++ b/pkg/keystone/models/projects.go @@ -19,10 +19,9 @@ import ( "database/sql" "fmt" - "github.com/pkg/errors" - "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/errors" "yunion.io/x/pkg/tristate" "yunion.io/x/sqlchemy" @@ -66,16 +65,12 @@ func init() { +-------------+-------------+------+-----+---------+-------+ */ -type SBaseProject struct { +type SProject struct { SIdentityBaseResource - ParentId string `width:"64" charset:"ascii" list:"admin" create:"admin_optional"` + ParentId string `width:"64" charset:"ascii" list:"domain" create:"domain_optional"` - IsDomain tristate.TriState `default:"false" nullable:"false" create:"admin_optional"` -} - -type SProject struct { - SBaseProject + IsDomain tristate.TriState `default:"false" nullable:"false" create:"domain_optional"` } func (manager *SProjectManager) GetContextManagers() [][]db.IModelManager { @@ -244,7 +239,7 @@ func (manager *SProjectManager) ListItemFilter(ctx context.Context, q *sqlchemy. func (model *SProject) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error { model.ParentId = ownerId.GetProjectDomainId() model.IsDomain = tristate.False - return model.SBaseProject.CustomizeCreate(ctx, userCred, ownerId, query, data) + return model.SIdentityBaseResource.CustomizeCreate(ctx, userCred, ownerId, query, data) } func (proj *SProject) GetUserCount() (int, error) { diff --git a/pkg/keystone/models/users.go b/pkg/keystone/models/users.go index 8730b1da29..69e969e11b 100644 --- a/pkg/keystone/models/users.go +++ b/pkg/keystone/models/users.go @@ -73,22 +73,22 @@ func init() { type SUser struct { SEnabledIdentityBaseResource - Email string `width:"64" charset:"ascii" nullable:"true" index:"true" list:"admin" update:"admin" create:"admin_optional"` - Mobile string `width:"20" charset:"ascii" nullable:"true" index:"true" list:"admin" update:"admin" create:"admin_optional"` + Email string `width:"64" charset:"ascii" nullable:"true" index:"true" list:"domain" update:"domain" create:"domain_optional"` + Mobile string `width:"20" charset:"ascii" nullable:"true" index:"true" list:"domain" update:"domain" create:"domain_optional"` - Displayname string `with:"128" charset:"utf8" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` + Displayname string `with:"128" charset:"utf8" nullable:"true" list:"domain" update:"domain" create:"domain_optional"` - LastActiveAt time.Time `nullable:"true" list:"admin"` + LastActiveAt time.Time `nullable:"true" list:"domain"` - LastLoginIp string `nullable:"true" list:"admin"` - LastLoginSource string `nullable:"true" list:"admin"` + LastLoginIp string `nullable:"true" list:"domain"` + LastLoginSource string `nullable:"true" list:"domain"` - IsSystemAccount tristate.TriState `nullable:"false" default:"false" list:"admin" update:"admin" create:"admin_optional"` + IsSystemAccount tristate.TriState `nullable:"false" default:"false" list:"domain" update:"domain" create:"domain_optional"` DefaultProjectId string `width:"64" charset:"ascii" nullable:"true"` - AllowWebConsole tristate.TriState `nullable:"false" default:"true" list:"admin" update:"admin" create:"admin_optional"` - EnableMfa tristate.TriState `nullable:"false" default:"true" list:"admin" update:"admin" create:"admin_optional"` + AllowWebConsole tristate.TriState `nullable:"false" default:"true" list:"domain" update:"domain" create:"domain_optional"` + EnableMfa tristate.TriState `nullable:"false" default:"true" list:"domain" update:"domain" create:"domain_optional"` } func (manager *SUserManager) GetContextManagers() [][]db.IModelManager { @@ -327,15 +327,21 @@ func (manager *SUserManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQu } func (user *SUser) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if user.IsAdminUser() { - return nil, httperrors.NewForbiddenError("system admin user is protected") - } - if user.IsReadOnly() { - return nil, httperrors.NewForbiddenError("readonly") + if data.Contains("name") { + if user.IsAdminUser() { + return nil, httperrors.NewForbiddenError("cannot alter sysadmin user name") + } } return user.SEnabledIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, data) } +func (user *SUser) ValidateUpdateCondition(ctx context.Context) error { + if user.IsReadOnly() { + return httperrors.NewForbiddenError("readonly") + } + return user.SEnabledIdentityBaseResource.ValidateUpdateCondition(ctx) +} + func (manager *SUserManager) fetchUserById(uid string) (*SUser, error) { obj, err := manager.FetchById(uid) if err != nil { diff --git a/pkg/mcclient/modules/mod_cloudaccounts.go b/pkg/mcclient/modules/mod_cloudaccounts.go index c2c7e22b0a..a327606165 100644 --- a/pkg/mcclient/modules/mod_cloudaccounts.go +++ b/pkg/mcclient/modules/mod_cloudaccounts.go @@ -24,7 +24,8 @@ func init() { "balance", "error_count", "health_status", "Sync_Status", "Last_sync", "guest_count", "domain", "domain_id", - "Provider", "Enable_Auto_Sync", "Sync_Interval_Seconds"}, + "Provider", "Brand", + "Enable_Auto_Sync", "Sync_Interval_Seconds"}, []string{}) registerCompute(&Cloudaccounts) diff --git a/pkg/mcclient/modules/mod_policies.go b/pkg/mcclient/modules/mod_policies.go index 6632e1b844..b0480bc332 100644 --- a/pkg/mcclient/modules/mod_policies.go +++ b/pkg/mcclient/modules/mod_policies.go @@ -29,7 +29,7 @@ var Policies SPolicyManager func policyReadFilter(session *mcclient.ClientSession, s jsonutils.JSONObject, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { ss := s.(*jsonutils.JSONDict) - ret := ss.CopyIncludes("id", "type", "enabled", "domain_id", "domain", "can_update", "can_delete") + ret := ss.CopyIncludes("id", "type", "enabled", "domain_id", "domain", "can_update", "can_delete", "is_public") blobJson, _ := ss.Get("blob") if blobJson != nil { policy := rbacutils.SRbacPolicy{} @@ -96,7 +96,7 @@ func policyWriteFilter(session *mcclient.ClientSession, s jsonutils.JSONObject, func init() { Policies = SPolicyManager{NewIdentityV3Manager("policy", "policies", - []string{"id", "type", "policy", "enabled", "domain_id", "domain"}, + []string{"id", "type", "policy", "enabled", "domain_id", "domain", "is_public"}, []string{})} Policies.SetReadFilter(policyReadFilter).SetWriteFilter(policyWriteFilter).SetNameField("type") diff --git a/pkg/util/rbacutils/rabc.go b/pkg/util/rbacutils/rbac.go similarity index 96% rename from pkg/util/rbacutils/rabc.go rename to pkg/util/rbacutils/rbac.go index 7abb8cd077..f7ef8b9292 100644 --- a/pkg/util/rbacutils/rabc.go +++ b/pkg/util/rbacutils/rbac.go @@ -90,6 +90,8 @@ type SRbacPolicy struct { DomainId string + IsPublic bool + Projects []string Roles []string Ips []netutils.IPV4Prefix @@ -550,6 +552,10 @@ type IRbacIdentity interface { GetLoginIp() string } +func (policy *SRbacPolicy) IsSystemWidePolicy() bool { + return len(policy.Roles) == 0 && len(policy.Projects) == 0 +} + func (policy *SRbacPolicy) Match(userCred IRbacIdentity) bool { if !policy.Auth && len(policy.Projects) == 0 && len(policy.Roles) == 0 && len(policy.Ips) == 0 { return true @@ -557,7 +563,10 @@ func (policy *SRbacPolicy) Match(userCred IRbacIdentity) bool { if userCred == nil { return false } - if (len(policy.Projects) == 0 || (policy.DomainId == userCred.GetProjectDomainId() && contains(policy.Projects, userCred.GetProjectName()))) && (len(policy.Roles) == 0 || (policy.DomainId == userCred.GetProjectDomainId() && intersect(policy.Roles, userCred.GetRoles()))) && (len(policy.Ips) == 0 || containsIp(policy.Ips, userCred.GetLoginIp())) { + if !policy.IsPublic && policy.DomainId != userCred.GetProjectDomainId() { + return false + } + if (len(policy.Projects) == 0 || contains(policy.Projects, userCred.GetProjectName())) && (len(policy.Roles) == 0 || intersect(policy.Roles, userCred.GetRoles())) && (len(policy.Ips) == 0 || containsIp(policy.Ips, userCred.GetLoginIp())) { return true } return false diff --git a/pkg/util/rbacutils/rabc_test.go b/pkg/util/rbacutils/rbac_test.go similarity index 100% rename from pkg/util/rbacutils/rabc_test.go rename to pkg/util/rbacutils/rbac_test.go diff --git a/pkg/yunionconf/models/parameters.go b/pkg/yunionconf/models/parameters.go index 6285f4a8fa..5cf38beb21 100644 --- a/pkg/yunionconf/models/parameters.go +++ b/pkg/yunionconf/models/parameters.go @@ -169,6 +169,10 @@ func (manager *SParameterManager) NamespaceScope() rbacutils.TRbacScope { return rbacutils.ScopeUser } +func (manager *SParameterManager) ResourceScope() rbacutils.TRbacScope { + return rbacutils.ScopeUser +} + func (manager *SParameterManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { if !isAdminQuery(query) { return true