Merge pull request #17047 from swordqiu/hotfix/qj-role-policy-assignment-violation-check

fix: prevent policy violate when assigning policy to roles
This commit is contained in:
Zexi Li
2023-05-16 11:11:56 +08:00
committed by GitHub
6 changed files with 104 additions and 26 deletions
+7 -1
View File
@@ -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)
}
+18
View File
@@ -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
}
+28 -13
View File
@@ -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,
+11 -1
View File
@@ -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
+39 -10
View File
@@ -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)
+1 -1
View File
@@ -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)