From ca0e3977c70cb4ad521e023098ae598f3cbfb4e2 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Thu, 15 Nov 2018 23:41:04 +0800 Subject: [PATCH] minor fixes --- pkg/cloudcommon/db/db_dispatcher.go | 68 ++++++++++++++++++----------- pkg/cloudcommon/db/taskman/tasks.go | 19 +++++--- pkg/cloudcommon/policy/policy.go | 57 +++++++++++++++++++++--- pkg/cloutpost/options/options.go | 21 +++++++++ pkg/cloutpost/service/handlers.go | 7 +++ pkg/cloutpost/service/service.go | 53 ++++++++++++++++++++++ pkg/util/rbacutils/rabc.go | 4 +- 7 files changed, 191 insertions(+), 38 deletions(-) create mode 100644 pkg/cloutpost/options/options.go create mode 100644 pkg/cloutpost/service/handlers.go create mode 100644 pkg/cloutpost/service/service.go diff --git a/pkg/cloudcommon/db/db_dispatcher.go b/pkg/cloudcommon/db/db_dispatcher.go index 1f4b543a69..89c08637c2 100644 --- a/pkg/cloudcommon/db/db_dispatcher.go +++ b/pkg/cloudcommon/db/db_dispatcher.go @@ -479,14 +479,14 @@ func listItems(manager IModelManager, ctx context.Context, userCred mcclient.Tok if err != nil { return nil, httperrors.NewGeneralError(err) } - retConut := len(retList) + retCount := len(retList) // apply customizeFilters retList, err = customizeFilters.DoApply(retList) if err != nil { return nil, httperrors.NewGeneralError(err) } - if len(retList) != retConut { + if len(retList) != retCount { totalCnt = int64(len(retList)) } paginate := false @@ -500,7 +500,7 @@ func listItems(manager IModelManager, ctx context.Context, userCred mcclient.Tok func calculateListResult(data []jsonutils.JSONObject, total, limit, offset int64, paginate bool) *modules.ListResult { if paginate { // do offset first - if offset != 0 { + if offset > 0 { if total > offset { data = data[offset:] } else { @@ -508,11 +508,13 @@ func calculateListResult(data []jsonutils.JSONObject, total, limit, offset int64 } } // do limit - if total > limit { + if limit > 0 && total > limit { data = data[:limit] } } + retResult := modules.ListResult{Data: data, Total: int(total), Limit: int(limit), Offset: int(offset)} + return &retResult } @@ -957,6 +959,34 @@ func (dispatcher *DBModelDispatcher) BatchCreate(ctx context.Context, query json return results, nil } +func managerPerformCheckCreateData( + manager IModelManager, + ctx context.Context, + userCred mcclient.TokenCredential, + action string, + ownerProjId string, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) (jsonutils.JSONObject, error) { + body, err := data.(*jsonutils.JSONDict).Get(manager.Keyword()) + if err != nil { + return nil, httperrors.NewGeneralError(err) + } + bodyDict := body.(*jsonutils.JSONDict) + + var isAllow bool + if consts.IsRbacEnabled() { + isAllow = isClassActionRbacAllowed(manager, userCred, ownerProjId, policy.PolicyActionPerform, action) + } else { + isAllow = manager.AllowPerformCheckCreateData(ctx, userCred, query, data) + } + if !isAllow { + return nil, httperrors.NewForbiddenError("not allow to perform %s", action) + } + + return manager.ValidateCreateData(ctx, userCred, ownerProjId, query, bodyDict) +} + func (dispatcher *DBModelDispatcher) PerformClassAction(ctx context.Context, action string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { userCred := fetchUserCredential(ctx) @@ -969,25 +999,8 @@ func (dispatcher *DBModelDispatcher) PerformClassAction(ctx context.Context, act defer lockman.ReleaseClass(ctx, dispatcher.modelManager, ownerProjId) if action == "check-create-data" { - manager := dispatcher.modelManager - - body, err := data.(*jsonutils.JSONDict).Get(manager.Keyword()) - if err != nil { - return nil, httperrors.NewGeneralError(err) - } - data := body.(*jsonutils.JSONDict) - - var isAllow bool - if consts.IsRbacEnabled() { - isAllow = isClassActionRbacAllowed(manager, userCred, ownerProjId, policy.PolicyActionPerform, action) - } else { - isAllow = manager.AllowPerformCheckCreateData(ctx, userCred, query, data) - } - if !isAllow { - return nil, httperrors.NewForbiddenError("not allow to perform %s", action) - } - - return manager.ValidateCreateData(ctx, userCred, ownerProjId, query, data) + return managerPerformCheckCreateData(dispatcher.modelManager, + ctx, userCred, action, ownerProjId, query, data) } managerValue := reflect.ValueOf(dispatcher.modelManager) @@ -1022,8 +1035,8 @@ func objectPerformAction(dispatcher *DBModelDispatcher, model IModel, modelValue isGeneral := false funcName := fmt.Sprintf("Perform%s", utils.Kebab2Camel(action, "-")) - funcValue := modelValue.MethodByName(funcName) + if !funcValue.IsValid() || funcValue.IsNil() { funcValue = modelValue.MethodByName(generalFuncName) if !funcValue.IsValid() || funcValue.IsNil() { @@ -1057,7 +1070,12 @@ func objectPerformAction(dispatcher *DBModelDispatcher, model IModel, modelValue var isAllow bool if consts.IsRbacEnabled() { - isAllow = isObjectRbacAllowed(dispatcher.modelManager, model, userCred, policy.PolicyActionPerform, action) + if model == nil { + ownerProjId, _ := fetchOwnerProjectId(ctx, dispatcher.modelManager, userCred, data) + isAllow = isClassActionRbacAllowed(dispatcher.modelManager, userCred, ownerProjId, policy.PolicyActionPerform, action) + } else { + isAllow = isObjectRbacAllowed(dispatcher.modelManager, model, userCred, policy.PolicyActionPerform, action) + } } else { allowFuncName := "Allow" + funcName allowFuncValue := modelValue.MethodByName(allowFuncName) diff --git a/pkg/cloudcommon/db/taskman/tasks.go b/pkg/cloudcommon/db/taskman/tasks.go index 9b4f175e46..5460fff7cc 100644 --- a/pkg/cloudcommon/db/taskman/tasks.go +++ b/pkg/cloudcommon/db/taskman/tasks.go @@ -89,10 +89,6 @@ func (manager *STaskManager) FilterByName(q *sqlchemy.SQuery, name string) *sqlc return q } -func (manager *STaskManager) FilterByOwner(q *sqlchemy.SQuery, owner string) *sqlchemy.SQuery { - return q -} - func (manager *STaskManager) AllowPerformAction(ctx context.Context, userCred mcclient.TokenCredential, action string, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return true } @@ -105,6 +101,19 @@ func (manager *STaskManager) PerformAction(ctx context.Context, userCred mcclien return resp, nil } +func (manager *STaskManager) GetOwnerId(userCred mcclient.IIdentityProvider) string { + return userCred.GetProjectId() +} + +func (self *STask) GetOwnerProjectId() string { + return self.UserCred.GetProjectId() +} + +func (manager *STaskManager) FilterByOwner(q *sqlchemy.SQuery, owner string) *sqlchemy.SQuery { + q = q.Contains("user_cred", owner) + return q +} + func (self *STask) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { return userCred.IsSystemAdmin() || userCred.GetProjectId() == self.UserCred.GetProjectId() } @@ -414,7 +423,7 @@ func execITask(taskValue reflect.Value, task *STask, odata jsonutils.JSONObject, return } - log.Debugf("Call %s %s: %s with %s", task.TaskName, stageName, funcValue, params) + log.Debugf("Call %s %s", task.TaskName, stageName) funcValue.Call(params) // call save request context diff --git a/pkg/cloudcommon/policy/policy.go b/pkg/cloudcommon/policy/policy.go index 623b2a7ee0..c5a14b5e64 100644 --- a/pkg/cloudcommon/policy/policy.go +++ b/pkg/cloudcommon/policy/policy.go @@ -30,8 +30,37 @@ const ( var ( PolicyManager *SPolicyManager - PolicyFailedRetryInterval = 15 * time.Second - PolicyRefreshInterval = 15 * time.Minute + defaultRules = []rbacutils.SRbacRule{ + { + Resource: "tasks", + Action: PolicyActionPerform, + Result: rbacutils.Allow, + }, + { + Service: "compute", + Resource: "zones", + Action: PolicyActionList, + Result: rbacutils.Allow, + }, + { + Service: "compute", + Resource: "zones", + Action: PolicyActionGet, + Result: rbacutils.Allow, + }, + { + Service: "compute", + Resource: "cloudregions", + Action: PolicyActionList, + Result: rbacutils.Allow, + }, + { + Service: "compute", + Resource: "cloudregions", + Action: PolicyActionGet, + Result: rbacutils.Allow, + }, + } ) func init() { @@ -41,7 +70,11 @@ func init() { type SPolicyManager struct { policies map[string]rbacutils.SRbacPolicy adminPolicies map[string]rbacutils.SRbacPolicy + defaultPolicy *rbacutils.SRbacPolicy lastSync time.Time + + failedRetryInterval time.Duration + refreshInterval time.Duration } func parseJsonPolicy(obj jsonutils.JSONObject) (string, rbacutils.SRbacPolicy, error) { @@ -114,8 +147,13 @@ func fetchPolicies() (map[string]rbacutils.SRbacPolicy, map[string]rbacutils.SRb func (manager *SPolicyManager) start(refreshInterval time.Duration, retryInterval time.Duration) { log.Infof("PolicyManager start to fetch policies ...") - PolicyRefreshInterval = refreshInterval - PolicyFailedRetryInterval = retryInterval + manager.refreshInterval = refreshInterval + manager.failedRetryInterval = retryInterval + if len(defaultRules) > 0 { + manager.defaultPolicy = &rbacutils.SRbacPolicy{ + Rules: rbacutils.CompactRules(defaultRules), + } + } manager.sync() } @@ -124,13 +162,14 @@ func (manager *SPolicyManager) sync() { policies, adminPolicies, err := fetchPolicies() if err != nil { log.Errorf("sync policy fail %s", err) - time.AfterFunc(PolicyFailedRetryInterval, manager.sync) + time.AfterFunc(manager.failedRetryInterval, manager.sync) return } manager.policies = policies manager.adminPolicies = adminPolicies + manager.lastSync = time.Now() - time.AfterFunc(PolicyRefreshInterval, manager.sync) + time.AfterFunc(manager.refreshInterval, manager.sync) } func (manager *SPolicyManager) Allow(isAdmin bool, userCred mcclient.TokenCredential, service string, resource string, action string, extra ...string) rbacutils.TRbacResult { @@ -152,6 +191,12 @@ func (manager *SPolicyManager) Allow(isAdmin bool, userCred mcclient.TokenCreden currentPriv = result } } + if manager.defaultPolicy != nil { + result := manager.defaultPolicy.Allow(userCredJson, service, resource, action, extra...) + if result.IsHigherPrivilege(currentPriv) { + currentPriv = result + } + } if consts.IsRbacDebug() { log.Debugf("[RBAC: %v] %s %s %s %#v permission %s", isAdmin, service, resource, action, extra, currentPriv) } diff --git a/pkg/cloutpost/options/options.go b/pkg/cloutpost/options/options.go new file mode 100644 index 0000000000..ec3a78860b --- /dev/null +++ b/pkg/cloutpost/options/options.go @@ -0,0 +1,21 @@ +package options + +import ( + "yunion.io/x/onecloud/pkg/cloudcommon" + "yunion.io/x/onecloud/pkg/cloudcommon/etcd" +) + +type CloudSyncOptions struct { + Provider string `help:"Public cloud provider" choices:"Aliyun|Azure|Aws|Qcloud"` + Environment string `help:"environment of public cloud"` + Cloudregion string `help:"region of public cloud"` + Zone string `help:"availability zone of public cloud"` + + etcd.SEtcdOptions + + cloudcommon.Options +} + +var ( + Options CloudSyncOptions +) diff --git a/pkg/cloutpost/service/handlers.go b/pkg/cloutpost/service/handlers.go new file mode 100644 index 0000000000..f1c362799b --- /dev/null +++ b/pkg/cloutpost/service/handlers.go @@ -0,0 +1,7 @@ +package service + +import "yunion.io/x/onecloud/pkg/appsrv" + +func initHandlers(app *appsrv.Application) { + +} diff --git a/pkg/cloutpost/service/service.go b/pkg/cloutpost/service/service.go new file mode 100644 index 0000000000..243e311ada --- /dev/null +++ b/pkg/cloutpost/service/service.go @@ -0,0 +1,53 @@ +package service + +import ( + "os" + "yunion.io/x/log" + + "yunion.io/x/onecloud/pkg/cloudcommon" + "yunion.io/x/onecloud/pkg/cloudcommon/consts" + "yunion.io/x/onecloud/pkg/cloudcommon/etcd" + "yunion.io/x/onecloud/pkg/cloudcommon/etcd/models" + "yunion.io/x/onecloud/pkg/cloutpost/options" +) + +const ( + SERVICE_TYPE = "cloutpost" +) + +func StartService() { + consts.SetServiceType(SERVICE_TYPE) + + cloudcommon.ParseOptions(&options.Options, &options.Options.Options, os.Args, "cloutpost.conf") + + cloudcommon.InitAuth(&options.Options.Options, func() { + log.Infof("Auth complete!!") + }) + + err := etcd.InitDefaultEtcdClient(&options.Options.SEtcdOptions) + if err != nil { + log.Fatalf("init etcd fail: %s", err) + } + defer etcd.CloseDefaultEtcdClient() + + app := cloudcommon.InitApp(&options.Options.Options) + + initHandlers(app) + + err = models.ServiceRegistryManager.Register( + app.GetContext(), + options.Options.Address, + options.Options.Port, + options.Options.Provider, + options.Options.Environment, + options.Options.Cloudregion, + options.Options.Zone, + SERVICE_TYPE, + ) + + if err != nil { + log.Fatalf("fail to register service %s", err) + } + + cloudcommon.ServeForever(app, &options.Options.Options) +} diff --git a/pkg/util/rbacutils/rabc.go b/pkg/util/rbacutils/rabc.go index ebf1fb911c..39530086ee 100644 --- a/pkg/util/rbacutils/rabc.go +++ b/pkg/util/rbacutils/rabc.go @@ -153,7 +153,7 @@ func (policy *SRbacPolicy) GetMatchRule(service string, resource string, action return matchRule } -func compactRules(rules []SRbacRule) []SRbacRule { +func CompactRules(rules []SRbacRule) []SRbacRule { output := make([]SRbacRule, 1) output[0] = rules[0] for i := 1; i < len(rules); i += 1 { @@ -190,7 +190,7 @@ func (policy *SRbacPolicy) Decode(policyJson jsonutils.JSONObject) error { return err } - policy.Rules = compactRules(rules) + policy.Rules = CompactRules(rules) return nil }