diff --git a/pkg/cloudcommon/app/options.go b/pkg/cloudcommon/app/options.go index a9ab5c03e4..5aad2341f4 100644 --- a/pkg/cloudcommon/app/options.go +++ b/pkg/cloudcommon/app/options.go @@ -19,6 +19,7 @@ import ( "net/http" "yunion.io/x/jsonutils" + "yunion.io/x/pkg/util/httputils" "yunion.io/x/pkg/util/rbacscope" "yunion.io/x/onecloud/pkg/appsrv" @@ -30,6 +31,10 @@ import ( ) func ExportOptionsHandler(app *appsrv.Application, options interface{}) { + ExportOptionsHandlerWithPrefix(app, "", options) +} + +func ExportOptionsHandlerWithPrefix(app *appsrv.Application, prefix string, options interface{}) { hf := func(ctx context.Context, w http.ResponseWriter, r *http.Request) { userCred := auth.FetchUserCredential(ctx, policy.FilterPolicyCredential) result := policy.PolicyManager.Allow(rbacscope.ScopeSystem, userCred, consts.GetServiceType(), "app-options", "list") @@ -41,5 +46,6 @@ func ExportOptionsHandler(app *appsrv.Application, options interface{}) { } ahf := auth.Authenticate(hf) name := "get_app_options" - app.AddHandler2("GET", "/app-options", ahf, nil, name, nil) + pref := httputils.JoinPath(prefix, "app-options") + app.AddHandler2("GET", pref, ahf, nil, name, nil) } diff --git a/pkg/keystone/models/policies.go b/pkg/keystone/models/policies.go index 9d72c0e81b..829508cacc 100644 --- a/pkg/keystone/models/policies.go +++ b/pkg/keystone/models/policies.go @@ -678,3 +678,21 @@ func (policy *SPolicy) GetI18N(ctx context.Context) *jsonutils.JSONDict { r.Set("description", jsonutils.NewString(act18)) return r } + +func (policy *SPolicy) ValidateUpdateCondition(ctx context.Context) error { + err := policy.SEnabledIdentityBaseResource.ValidateUpdateCondition(ctx) + if err != nil { + return errors.Wrap(err, "SEnabledIdentityBaseResource.ValidateUpdateCondition") + } + //if policy.IsSystem.IsTrue() { + // return errors.Wrap(httperrors.ErrForbidden, "system policy") + //} + rps, err := RolePolicyManager.fetchByPolicyId(policy.Id) + if err != nil { + return errors.Wrap(err, "fetchByPolicyId") + } + if len(rps) > 0 { + return errors.Wrap(httperrors.ErrForbidden, "policy in use") + } + return nil +} diff --git a/pkg/keystone/models/projects.go b/pkg/keystone/models/projects.go index ae8d7c6286..b516014821 100644 --- a/pkg/keystone/models/projects.go +++ b/pkg/keystone/models/projects.go @@ -37,6 +37,7 @@ import ( "yunion.io/x/onecloud/pkg/keystone/options" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/logclient" + "yunion.io/x/onecloud/pkg/util/rbacutils" "yunion.io/x/onecloud/pkg/util/stringutils2" "yunion.io/x/onecloud/pkg/util/tagutils" ) @@ -529,14 +530,14 @@ func (manager *SProjectManager) ValidateCreateData(ctx context.Context, userCred return input, nil } -func (self *SProject) PostCreate( +func (project *SProject) PostCreate( ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject, ) { - self.SIdentityBaseResource.PostCreate(ctx, userCred, ownerId, query, data) + project.SIdentityBaseResource.PostCreate(ctx, userCred, ownerId, query, data) quota := &SIdentityQuota{Project: 1} quota.SetKeys(quotas.SBaseDomainQuotaKeys{DomainId: ownerId.GetProjectDomainId()}) @@ -546,8 +547,7 @@ func (self *SProject) PostCreate( } } -func threeMemberSystemValidateJoinProject(userCred mcclient.TokenCredential, project *SProject, roleIds []string) error { - _, assignPolicies, _ := RolePolicyManager.GetMatchPolicyGroup2(false, roleIds, project.Id, "", time.Time{}, false) +func threeMemberSystemValidatePolicies(userCred mcclient.TokenCredential, projectId string, assignPolicies rbacutils.TPolicyGroup) error { assignScope := assignPolicies.HighestScope() var checkRoles []string if assignScope == rbacscope.ScopeSystem { @@ -563,7 +563,7 @@ func threeMemberSystemValidateJoinProject(userCred mcclient.TokenCredential, pro if err != nil { return httperrors.NewResourceNotFoundError2(RoleManager.Keyword(), roleName) } - _, adminPolicies, _ := RolePolicyManager.GetMatchPolicyGroup2(false, []string{role.Id}, project.Id, "", time.Time{}, false) + _, adminPolicies, _ := RolePolicyManager.GetMatchPolicyGroup2(false, []string{role.Id}, projectId, "", time.Time{}, false) if adminPolicies[assignScope].Contains(assignPolicies[assignScope]) { contains = append(contains, roleName) } @@ -574,15 +574,11 @@ func threeMemberSystemValidateJoinProject(userCred mcclient.TokenCredential, pro return nil } -func validateJoinProject(userCred mcclient.TokenCredential, project *SProject, roleIds []string) error { - if options.Options.NoPolicyViolationCheck { - return nil +func normalValidatePolicies(userCred mcclient.TokenCredential, assignPolicies rbacutils.TPolicyGroup) error { + _, opsPolicies, err := RolePolicyManager.GetMatchPolicyGroup(userCred, time.Time{}, false) + if err != nil { + return errors.Wrap(err, "RolePolicyManager.GetMatchPolicyGroup") } - if options.Options.ThreeAdminRoleSystem { - return threeMemberSystemValidateJoinProject(userCred, project, roleIds) - } - _, opsPolicies, _ := RolePolicyManager.GetMatchPolicyGroup(userCred, time.Time{}, false) - _, assignPolicies, _ := RolePolicyManager.GetMatchPolicyGroup2(false, roleIds, project.Id, "", time.Time{}, false) opsScope := opsPolicies.HighestScope() assignScope := assignPolicies.HighestScope() if assignScope.HigherThan(opsScope) { @@ -593,6 +589,25 @@ func validateJoinProject(userCred mcclient.TokenCredential, project *SProject, r return nil } +func validateAssignPolicies(userCred mcclient.TokenCredential, projectId string, assignPolicies rbacutils.TPolicyGroup) error { + if options.Options.NoPolicyViolationCheck { + return nil + } + if options.Options.ThreeAdminRoleSystem { + return threeMemberSystemValidatePolicies(userCred, projectId, assignPolicies) + } else { + return normalValidatePolicies(userCred, assignPolicies) + } +} + +func validateJoinProject(userCred mcclient.TokenCredential, project *SProject, roleIds []string) error { + _, assignPolicies, err := RolePolicyManager.GetMatchPolicyGroup2(false, roleIds, project.Id, "", time.Time{}, false) + if err != nil { + return errors.Wrap(err, "RolePolicyManager.GetMatchPolicyGroup2") + } + return validateAssignPolicies(userCred, project.Id, assignPolicies) +} + func (project *SProject) AllowPerformJoin(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, diff --git a/pkg/keystone/models/rolepolicies.go b/pkg/keystone/models/rolepolicies.go index 0a453dc7f3..72eae6e084 100644 --- a/pkg/keystone/models/rolepolicies.go +++ b/pkg/keystone/models/rolepolicies.go @@ -565,7 +565,17 @@ func (manager *SRolePolicyManager) fetchByRoleId(roleId string) ([]SRolePolicy, q := manager.Query().Equals("role_id", roleId) rps := make([]SRolePolicy, 0) err := db.FetchModelObjects(manager, q, &rps) - if err != nil && errors.Cause(err) == sql.ErrNoRows { + if err != nil { + return nil, errors.Wrap(err, "FetchModelObjects") + } + return rps, nil +} + +func (manager *SRolePolicyManager) fetchByPolicyId(policyId string) ([]SRolePolicy, error) { + q := manager.Query().Equals("policy_id", policyId) + rps := make([]SRolePolicy, 0) + err := db.FetchModelObjects(manager, q, &rps) + if err != nil { return nil, errors.Wrap(err, "FetchModelObjects") } return rps, nil diff --git a/pkg/keystone/models/roles.go b/pkg/keystone/models/roles.go index 0b24341429..06f555c821 100644 --- a/pkg/keystone/models/roles.go +++ b/pkg/keystone/models/roles.go @@ -548,14 +548,19 @@ func (role *SRole) GetSharedDomains() []string { return db.SharableGetSharedProjects(role, db.SharedTargetDomain) } -func (role *SRole) AllowPerformSetPolicies(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RolePerformSetPoliciesInput) bool { - return true +func validateRolePolicies(userCred mcclient.TokenCredential, policyIds []string) error { + _, assignPolicies, err := RolePolicyManager.GetPolicyGroupByIds(policyIds, false) + if err != nil { + return errors.Wrapf(err, "RolePolicyManager.GetPolicyGroupByIds %s", policyIds) + } + return validateAssignPolicies(userCred, "", assignPolicies) } func (role *SRole) PerformSetPolicies(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RolePerformSetPoliciesInput) (jsonutils.JSONObject, error) { if len(input.Action) == 0 { input.Action = api.ROLE_SET_POLICY_ACTION_DEFAULT } + inputPolicyIds := stringutils2.NewSortedStrings(nil) normalInputIds := stringutils2.NewSortedStrings(nil) normalInputs := make(map[string]sRolePerformAddPolicyInput, len(input.Policies)) for i := range input.Policies { @@ -563,6 +568,7 @@ func (role *SRole) PerformSetPolicies(ctx context.Context, userCred mcclient.Tok if err != nil { return nil, errors.Wrapf(err, "normalizeRoleAddPolicyInput at %d", i) } + inputPolicyIds = stringutils2.Append(inputPolicyIds, normalInput.policyId) idstr := normalInput.getId() if _, ok := normalInputs[idstr]; !ok { normalInputs[idstr] = normalInput @@ -577,9 +583,11 @@ func (role *SRole) PerformSetPolicies(ctx context.Context, userCred mcclient.Tok return nil, errors.Wrap(err, "RolePolicyManager.fetchByRoleId") } + existPolicyIds := stringutils2.NewSortedStrings(nil) existRpIds := stringutils2.NewSortedStrings(nil) existRpMap := make(map[string]*SRolePolicy) for i := range existRpList { + existPolicyIds = stringutils2.Append(existPolicyIds, existRpList[i].PolicyId) idstr := existRpList[i].GetId() if _, ok := existRpMap[idstr]; !ok { existRpMap[idstr] = &existRpList[i] @@ -589,6 +597,19 @@ func (role *SRole) PerformSetPolicies(ctx context.Context, userCred mcclient.Tok addedIds, updatedIds, deletedIds := stringutils2.Split(normalInputIds, existRpIds) + // validate + var newPolicyIds []string + if input.Action == api.ROLE_SET_POLICY_ACTION_REPLACE { + newPolicyIds = inputPolicyIds + } else { + newPolicyIds = stringutils2.Append(newPolicyIds, inputPolicyIds...) + newPolicyIds = stringutils2.Append(newPolicyIds, existPolicyIds...) + } + err = validateRolePolicies(userCred, newPolicyIds) + if err != nil { + return nil, errors.Wrap(err, "validateRolePolicies") + } + if input.Action == api.ROLE_SET_POLICY_ACTION_REPLACE { for _, idstr := range deletedIds { toDel := existRpMap[idstr] @@ -618,10 +639,6 @@ func (role *SRole) PerformSetPolicies(ctx context.Context, userCred mcclient.Tok return nil, nil } -func (role *SRole) AllowPerformAddPolicy(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RolePerformAddPolicyInput) bool { - return true -} - type sRolePerformAddPolicyInput struct { prefixes []netutils.IPV4Prefix roleId string @@ -681,6 +698,22 @@ func (role *SRole) PerformAddPolicy(ctx context.Context, userCred mcclient.Token if err != nil { return nil, errors.Wrap(err, "normalizeRoleAddPolicyInput") } + + // validate + rps, err := RolePolicyManager.fetchByRoleId(role.Id) + if err != nil { + return nil, errors.Wrap(err, "fetchByRoleId") + } + newPolicyIds := stringutils2.NewSortedStrings(nil) + for i := range rps { + newPolicyIds = stringutils2.Append(newPolicyIds, rps[i].PolicyId) + } + newPolicyIds = stringutils2.Append(newPolicyIds, normalInput.policyId) + err = validateRolePolicies(userCred, newPolicyIds) + if err != nil { + return nil, errors.Wrap(err, "validateRolePolicies") + } + err = RolePolicyManager.newRecord(ctx, normalInput.roleId, normalInput.projectId, normalInput.policyId, tristate.True, normalInput.prefixes, normalInput.validSince, normalInput.validUntil) if err != nil { return nil, errors.Wrap(err, "newRecord") @@ -688,10 +721,6 @@ func (role *SRole) PerformAddPolicy(ctx context.Context, userCred mcclient.Token return nil, nil } -func (role *SRole) AllowPerformRemovePolicy(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RolePerformRemovePolicyInput) bool { - return true -} - func (role *SRole) PerformRemovePolicy(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RolePerformRemovePolicyInput) (jsonutils.JSONObject, error) { if len(input.ProjectId) > 0 { proj, err := ProjectManager.FetchByIdOrName(userCred, input.ProjectId) diff --git a/pkg/keystone/service/handlers.go b/pkg/keystone/service/handlers.go index af5050d998..ea04da6c39 100644 --- a/pkg/keystone/service/handlers.go +++ b/pkg/keystone/service/handlers.go @@ -44,7 +44,7 @@ func InitHandlers(app *appsrv.Application) { usages.AddUsageHandler(API_VERSION, app) taskman.AddTaskHandler(API_VERSION, app) - app_common.ExportOptionsHandler(app, &options.Options) + app_common.ExportOptionsHandlerWithPrefix(app, API_VERSION, &options.Options) tokens.AddHandler(app)