mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-30 17:13:08 +08:00
feature: policy group support
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
# rbac for project owner, not allow for delete
|
||||
roles:
|
||||
- domain_admin
|
||||
scope: domain
|
||||
policy:
|
||||
'*': allow
|
||||
+4
-3
@@ -1,9 +1,10 @@
|
||||
# rbac for normal user, not allow for delete
|
||||
scope: project
|
||||
policy:
|
||||
'*':
|
||||
'*':
|
||||
'*': allow
|
||||
create: deny
|
||||
update: deny
|
||||
delete: deny
|
||||
perform:
|
||||
purge: deny
|
||||
clone: deny
|
||||
'*': allow
|
||||
@@ -0,0 +1,2 @@
|
||||
policy:
|
||||
'*': allow
|
||||
@@ -1,6 +0,0 @@
|
||||
# rbac for project owner, not allow for delete
|
||||
roles:
|
||||
- project_owner
|
||||
scope: project
|
||||
policy:
|
||||
'*': allow
|
||||
@@ -1,8 +0,0 @@
|
||||
# rbac for sysadmin
|
||||
projects:
|
||||
- system
|
||||
roles:
|
||||
- admin
|
||||
scope: system
|
||||
policy:
|
||||
'*': allow
|
||||
@@ -0,0 +1,6 @@
|
||||
policy:
|
||||
'*':
|
||||
'*':
|
||||
get: allow
|
||||
list: allow
|
||||
'*': deny
|
||||
@@ -38,6 +38,7 @@ func init() {
|
||||
type PolicyListOptions struct {
|
||||
options.BaseListOptions
|
||||
Type string `help:"filter by type"`
|
||||
IsSystem *bool `help:"filter by is_system" negative:"is_no_system"`
|
||||
Format string `help:"policy format, default to yaml" default:"yaml" choices:"yaml|json"`
|
||||
OrderByDomain string `help:"order by domain name" choices:"asc|desc"`
|
||||
}
|
||||
@@ -100,6 +101,7 @@ func init() {
|
||||
Enabled bool `help:"update policy enabled"`
|
||||
Disabled bool `help:"update policy disabled"`
|
||||
Desc string `help:"Description"`
|
||||
IsSystem *bool `help:"is_system"`
|
||||
}
|
||||
updateFunc := func(s *mcclient.ClientSession, args *PolicyPatchOptions) error {
|
||||
policyId, err := modules.Policies.GetId(s, args.ID, nil)
|
||||
@@ -125,6 +127,13 @@ func init() {
|
||||
if len(args.Desc) > 0 {
|
||||
params.Add(jsonutils.NewString(args.Desc), "description")
|
||||
}
|
||||
if args.IsSystem != nil {
|
||||
if *args.IsSystem {
|
||||
params.Add(jsonutils.JSONTrue, "is_system")
|
||||
} else {
|
||||
params.Add(jsonutils.JSONFalse, "is_system")
|
||||
}
|
||||
}
|
||||
result, err := modules.Policies.Patch(s, policyId, params)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -236,11 +245,12 @@ func init() {
|
||||
UserDomain string `help:"Domain for user"`
|
||||
Project string `help:"Role assignments for project"`
|
||||
ProjectDomain string `help:"Domain for project"`
|
||||
Role []string `help:"Roles"`
|
||||
Role []string `help:"Role name list"`
|
||||
RoleId []string `help:"Role Id list"`
|
||||
}
|
||||
R(&PolicyAdminCapableOptions{}, "policy-admin-capable", "Check admin capable", func(s *mcclient.ClientSession, args *PolicyAdminCapableOptions) error {
|
||||
auth.InitFromClientSession(s)
|
||||
policy.EnableGlobalRbac(15*time.Second, 15*time.Second, false)
|
||||
policy.EnableGlobalRbac(15*time.Second, false)
|
||||
|
||||
var token mcclient.TokenCredential
|
||||
if len(args.User) > 0 {
|
||||
@@ -250,6 +260,7 @@ func init() {
|
||||
Project: args.Project,
|
||||
ProjectDomain: args.ProjectDomain,
|
||||
Roles: strings.Join(args.Role, ","),
|
||||
RoleIds: strings.Join(args.RoleId, ","),
|
||||
}
|
||||
} else {
|
||||
token = s.GetToken()
|
||||
@@ -277,6 +288,7 @@ func init() {
|
||||
UserDomain string `help:"Domain for user"`
|
||||
Project string `help:"Role assignments for project"`
|
||||
Role []string `help:"Roles"`
|
||||
RoleId []string `help:"Role Id list"`
|
||||
Request []string `help:"explain request, in format of key:scope:service:resource:action:extra"`
|
||||
Name string `help:"policy name"`
|
||||
Debug bool `help:"enable RBAC debug"`
|
||||
@@ -292,26 +304,11 @@ func init() {
|
||||
rbacutils.ShowMatchRuleDebug = true
|
||||
}
|
||||
auth.InitFromClientSession(s)
|
||||
policy.EnableGlobalRbac(15*time.Second, 15*time.Second, false)
|
||||
policy.EnableGlobalRbac(15*time.Second, false)
|
||||
if args.Debug {
|
||||
consts.EnableRbacDebug()
|
||||
}
|
||||
|
||||
findPolicy := false
|
||||
for !findPolicy {
|
||||
all := policy.PolicyManager.AllPolicies()
|
||||
for _, allP := range all {
|
||||
if len(allP) > 0 {
|
||||
findPolicy = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if findPolicy {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
req := jsonutils.NewDict()
|
||||
for i := 0; i < len(args.Request); i += 1 {
|
||||
parts := strings.Split(args.Request[i], ":")
|
||||
@@ -374,6 +371,7 @@ func init() {
|
||||
ProjectDomain: projDom,
|
||||
ProjectDomainId: projDomId,
|
||||
Roles: strings.Join(args.Role, ","),
|
||||
RoleIds: strings.Join(args.RoleId, ","),
|
||||
Context: mcclient.SAuthContext{
|
||||
Ip: args.Ip,
|
||||
},
|
||||
@@ -389,7 +387,7 @@ func init() {
|
||||
}
|
||||
printObject(result)
|
||||
|
||||
for _, r := range args.Role {
|
||||
/*for _, r := range args.Role {
|
||||
fmt.Println("role", r, "matched policies:", policy.PolicyManager.RoleMatchPolicies(r))
|
||||
}
|
||||
|
||||
@@ -405,7 +403,7 @@ func init() {
|
||||
fmt.Println("matched", scope, "policies:", m)
|
||||
}
|
||||
|
||||
fmt.Println("all_policies", policy.PolicyManager.AllPolicies())
|
||||
fmt.Println("all_policies", policy.PolicyManager.AllPolicies())*/
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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 identity
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type RolePolicyListOptions struct {
|
||||
api.RolePolicyListInput
|
||||
}
|
||||
R(&RolePolicyListOptions{}, "role-policy-list", "List associated policies of a role", func(s *mcclient.ClientSession, args *RolePolicyListOptions) error {
|
||||
results, err := modules.RolePolicies.List(s, jsonutils.Marshal(args))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(results, modules.RolePolicies.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
|
||||
type RoleAddPolicyOptions struct {
|
||||
ID string `json:"-" help:"role id or name to add policy"`
|
||||
api.RolePerformAddPolicyInput
|
||||
}
|
||||
R(&RoleAddPolicyOptions{}, "role-add-policy", "Add policy to a role", func(s *mcclient.ClientSession, args *RoleAddPolicyOptions) error {
|
||||
result, err := modules.RolesV3.PerformAction(s, args.ID, "add-policy", jsonutils.Marshal(args))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
type RoleRemovePolicyOptions struct {
|
||||
ID string `json:"-" help:"role id or name to remove policy"`
|
||||
api.RolePerformRemovePolicyInput
|
||||
}
|
||||
R(&RoleRemovePolicyOptions{}, "role-remove-policy", "Remove policy from a role", func(s *mcclient.ClientSession, args *RoleRemovePolicyOptions) error {
|
||||
result, err := modules.RolesV3.PerformAction(s, args.ID, "remove-policy", jsonutils.Marshal(args))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -615,7 +615,7 @@ func (this *projectRoles) add(roleId, roleName string) {
|
||||
this.roles = append(this.roles, role{id: roleId, name: roleName})
|
||||
}
|
||||
|
||||
func (this *projectRoles) getToken(scope rbacutils.TRbacScope, user, userId, domain, domainId string, ip string) mcclient.TokenCredential {
|
||||
func (this *projectRoles) getToken(user, userId, domain, domainId string, ip string) mcclient.TokenCredential {
|
||||
return &mcclient.SSimpleToken{
|
||||
Token: "faketoken",
|
||||
Domain: domain,
|
||||
@@ -627,11 +627,11 @@ func (this *projectRoles) getToken(scope rbacutils.TRbacScope, user, userId, dom
|
||||
ProjectDomain: this.domain,
|
||||
ProjectDomainId: this.domainId,
|
||||
Roles: strings.Join(this.getRoles(), ","),
|
||||
RoleIds: strings.Join(this.getRoleIds(), ","),
|
||||
Context: mcclient.SAuthContext{
|
||||
Ip: ip,
|
||||
},
|
||||
}
|
||||
// return policy.PolicyManager.IsScopeCapable(&t, scope)
|
||||
}
|
||||
|
||||
func (this *projectRoles) getRoles() []string {
|
||||
@@ -642,43 +642,55 @@ func (this *projectRoles) getRoles() []string {
|
||||
return roles
|
||||
}
|
||||
|
||||
func (this *projectRoles) json(user, userId, domain, domainId string, ip string) jsonutils.JSONObject {
|
||||
func (this *projectRoles) getRoleIds() []string {
|
||||
roles := make([]string, 0)
|
||||
for _, r := range this.roles {
|
||||
roles = append(roles, r.id)
|
||||
}
|
||||
return roles
|
||||
}
|
||||
|
||||
func (this *projectRoles) json(s *mcclient.ClientSession, user, userId, domain, domainId string, ip string) (jsonutils.JSONObject, map[string][]string) {
|
||||
obj := jsonutils.NewDict()
|
||||
obj.Add(jsonutils.NewString(this.id), "id")
|
||||
obj.Add(jsonutils.NewString(this.name), "name")
|
||||
obj.Add(jsonutils.NewString(this.domain), "domain")
|
||||
obj.Add(jsonutils.NewString(this.domainId), "domain_id")
|
||||
roleIds := make([]string, 0)
|
||||
roles := jsonutils.NewArray()
|
||||
for _, r := range this.roles {
|
||||
role := jsonutils.NewDict()
|
||||
role.Add(jsonutils.NewString(r.id), "id")
|
||||
role.Add(jsonutils.NewString(r.name), "name")
|
||||
roles.Add(role)
|
||||
roleIds = append(roleIds, r.id)
|
||||
}
|
||||
obj.Add(roles, "roles")
|
||||
|
||||
policies, _ := modules.RolePolicies.FetchMatchedPolicies(s, roleIds, this.id, ip)
|
||||
for _, scope := range []rbacutils.TRbacScope{
|
||||
rbacutils.ScopeProject,
|
||||
rbacutils.ScopeDomain,
|
||||
rbacutils.ScopeSystem,
|
||||
} {
|
||||
token := this.getToken(scope, user, userId, domain, domainId, ip)
|
||||
matches := policy.PolicyManager.MatchedPolicyNames(scope, token)
|
||||
obj.Add(jsonutils.NewStringArray(matches), fmt.Sprintf("%s_policies", scope))
|
||||
if len(matches) > 0 {
|
||||
obj.Add(jsonutils.JSONTrue, fmt.Sprintf("%s_capable", scope))
|
||||
} else {
|
||||
obj.Add(jsonutils.JSONFalse, fmt.Sprintf("%s_capable", scope))
|
||||
}
|
||||
// backward compatible
|
||||
if scope == rbacutils.ScopeSystem {
|
||||
if matches, ok := policies[string(scope)]; ok {
|
||||
obj.Add(jsonutils.NewStringArray(matches), fmt.Sprintf("%s_policies", scope))
|
||||
if len(matches) > 0 {
|
||||
obj.Add(jsonutils.JSONTrue, "admin_capable")
|
||||
obj.Add(jsonutils.JSONTrue, fmt.Sprintf("%s_capable", scope))
|
||||
} else {
|
||||
obj.Add(jsonutils.JSONFalse, "admin_capable")
|
||||
obj.Add(jsonutils.JSONFalse, fmt.Sprintf("%s_capable", scope))
|
||||
}
|
||||
// backward compatible
|
||||
if scope == rbacutils.ScopeSystem {
|
||||
if len(matches) > 0 {
|
||||
obj.Add(jsonutils.JSONTrue, "admin_capable")
|
||||
} else {
|
||||
obj.Add(jsonutils.JSONFalse, "admin_capable")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj
|
||||
return obj, policies
|
||||
}
|
||||
|
||||
func isLBAgentExists(s *mcclient.ClientSession) (bool, error) {
|
||||
@@ -850,39 +862,41 @@ func getUserInfo2(s *mcclient.ClientSession, uid string, pid string, loginIp str
|
||||
}
|
||||
|
||||
data.Add(jsonutils.NewStringArray(currentRoles), "roles")
|
||||
|
||||
var policies map[string][]string
|
||||
projJson := jsonutils.NewArray()
|
||||
for _, proj := range projects {
|
||||
projJson.Add(proj.json(
|
||||
j, p := proj.json(
|
||||
s,
|
||||
usrName,
|
||||
usrId,
|
||||
usrDomainName,
|
||||
usrDomainId,
|
||||
loginIp,
|
||||
))
|
||||
)
|
||||
projJson.Add(j)
|
||||
if proj.id == pid {
|
||||
policies = p
|
||||
}
|
||||
}
|
||||
data.Add(projJson, "projects")
|
||||
|
||||
if len(pid) > 0 {
|
||||
ident := rbacutils.NewRbacIdentity2(projDomainId, projName, currentRoles, loginIp)
|
||||
for _, scope := range []rbacutils.TRbacScope{
|
||||
rbacutils.ScopeSystem,
|
||||
rbacutils.ScopeDomain,
|
||||
rbacutils.ScopeProject,
|
||||
} {
|
||||
p := policy.PolicyManager.MatchedPolicyNames(scope, ident)
|
||||
data.Add(jsonutils.NewStringArray(p), fmt.Sprintf("%s_policies", scope))
|
||||
if scope == rbacutils.ScopeSystem {
|
||||
data.Add(jsonutils.NewStringArray(p), "admin_policies")
|
||||
} else if scope == rbacutils.ScopeProject {
|
||||
data.Add(jsonutils.NewStringArray(p), "policies")
|
||||
if p, ok := policies[string(scope)]; ok {
|
||||
data.Add(jsonutils.NewStringArray(p), fmt.Sprintf("%s_policies", scope))
|
||||
if scope == rbacutils.ScopeSystem {
|
||||
data.Add(jsonutils.NewStringArray(p), "admin_policies")
|
||||
} else if scope == rbacutils.ScopeProject {
|
||||
data.Add(jsonutils.NewStringArray(p), "policies")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allPolicies := policy.PolicyManager.AllPolicies()
|
||||
data.Add(jsonutils.Marshal(allPolicies), "all_policies")
|
||||
|
||||
services := jsonutils.NewArray()
|
||||
menus := jsonutils.NewArray()
|
||||
k8s := jsonutils.NewArray()
|
||||
|
||||
@@ -17,44 +17,27 @@ package policy
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
|
||||
func PolicyCreate(s *mcclient.ClientSession, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
/*params := jsonutils.NewDict()
|
||||
params.Add(jsonutils.NewString(args.TYPE), "type")
|
||||
params.Add(jsonutils.NewString(policy.String()), "policy")
|
||||
*/
|
||||
result, err := modules.Policies.Create(s, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policy.PolicyManager.SyncOnce()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func PolicyPatch(s *mcclient.ClientSession, idstr string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
/*params := jsonutils.NewDict()
|
||||
if len(name) > 0 {
|
||||
params.Add(jsonutils.NewString(name), "type")
|
||||
}
|
||||
if policy != nil {
|
||||
params.Add(jsonutils.NewString(policy.String()), "policy")
|
||||
}*/
|
||||
result, err := modules.Policies.Patch(s, idstr, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policy.PolicyManager.SyncOnce()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func PolicyDelete(s *mcclient.ClientSession, idstr string) error {
|
||||
_, err := modules.Policies.Delete(s, idstr, nil)
|
||||
if err != nil {
|
||||
policy.PolicyManager.SyncOnce()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package identity
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
|
||||
type SIdentityObject struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@@ -47,16 +49,13 @@ type SRoleAssignment struct {
|
||||
}
|
||||
|
||||
// rbacutils.IRbacIdentity interfaces
|
||||
func (ra *SRoleAssignment) GetProjectDomainId() string {
|
||||
return ra.Scope.Project.Domain.Id
|
||||
|
||||
func (ra *SRoleAssignment) GetProjectId() string {
|
||||
return ra.Scope.Project.Id
|
||||
}
|
||||
|
||||
func (ra *SRoleAssignment) GetProjectName() string {
|
||||
return ra.Scope.Project.Name
|
||||
}
|
||||
|
||||
func (ra *SRoleAssignment) GetRoles() []string {
|
||||
return []string{ra.Role.Name}
|
||||
func (ra *SRoleAssignment) GetRoleIds() []string {
|
||||
return []string{ra.Role.Id}
|
||||
}
|
||||
|
||||
func (ra *SRoleAssignment) GetLoginIp() string {
|
||||
@@ -64,7 +63,7 @@ func (ra *SRoleAssignment) GetLoginIp() string {
|
||||
}
|
||||
|
||||
func (ra *SRoleAssignment) GetTokenString() string {
|
||||
return "faketoken"
|
||||
return rbacutils.FAKE_TOKEN
|
||||
}
|
||||
|
||||
type RAInputObject struct {
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
)
|
||||
|
||||
type IdentityBaseResourceCreateInput struct {
|
||||
@@ -336,6 +337,9 @@ type PolicyListInput struct {
|
||||
|
||||
// 以类型查询
|
||||
Type []string `json:"type"`
|
||||
|
||||
// 是否显示系统权限
|
||||
IsSystem *bool `json:"is_system"`
|
||||
}
|
||||
|
||||
type RegionFilterListInput struct {
|
||||
@@ -397,9 +401,18 @@ type IdentityProviderUpdateInput struct {
|
||||
type PolicyUpdateInput struct {
|
||||
EnabledIdentityBaseUpdateInput
|
||||
|
||||
// Deprecated
|
||||
// swagger:ignore
|
||||
Type string `json:"type"`
|
||||
|
||||
// Policy内容
|
||||
Blob jsonutils.JSONObject `json:"blob"`
|
||||
|
||||
// 生效范围,project|domain|system
|
||||
Scope rbacutils.TRbacScope `json:"scope"`
|
||||
|
||||
// 是否为系统权限
|
||||
IsSystem *bool `json:"is_system"`
|
||||
}
|
||||
|
||||
type ProjectUpdateInput struct {
|
||||
@@ -475,9 +488,18 @@ type PolicyCreateInput struct {
|
||||
EnabledIdentityBaseResourceCreateInput
|
||||
apis.SharableResourceBaseCreateInput
|
||||
|
||||
// Deprecated
|
||||
// swagger:ignore
|
||||
Type string `json:"type"`
|
||||
|
||||
// policy
|
||||
Blob jsonutils.JSONObject `json:"blob"`
|
||||
|
||||
// 生效范围,project|domain|system
|
||||
Scope rbacutils.TRbacScope `json:"scope"`
|
||||
|
||||
// 是否为系统权限
|
||||
IsSystem *bool `json:"is_system"`
|
||||
}
|
||||
|
||||
type RoleCreateInput struct {
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
|
||||
package identity
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/apis"
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
)
|
||||
|
||||
type RoleDetails struct {
|
||||
IdentityBaseResourceDetails
|
||||
@@ -22,8 +25,11 @@ type RoleDetails struct {
|
||||
|
||||
SRole
|
||||
|
||||
UserCount int `json:"user_count"`
|
||||
GroupCount int `json:"group_count"`
|
||||
ProjectCount int `json:"project_count"`
|
||||
UserCount int `json:"user_count"`
|
||||
GroupCount int `json:"group_count"`
|
||||
ProjectCount int `json:"project_count"`
|
||||
|
||||
MatchPolicies []string `json:"match_policies"`
|
||||
|
||||
Policies map[rbacutils.TRbacScope][]string `json:"policies"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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 identity
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
)
|
||||
|
||||
type RolePolicyListInput struct {
|
||||
apis.ResourceBaseListInput
|
||||
|
||||
RoleIds []string `json:"role_ids"`
|
||||
|
||||
ProjectId string `json:"project_id"`
|
||||
|
||||
PolicyId string `json:"policy_id"`
|
||||
|
||||
Auth *bool `json:"auth"`
|
||||
}
|
||||
|
||||
type RolePolicyDetails struct {
|
||||
apis.ResourceBaseDetails
|
||||
|
||||
Id string `json:"id"`
|
||||
|
||||
Name string `json:"name"`
|
||||
|
||||
Role string `json:"role"`
|
||||
|
||||
Project string `json:"project"`
|
||||
|
||||
Policy string `json:"policy"`
|
||||
|
||||
Scope rbacutils.TRbacScope `json:"scope"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type RolePerformSetPoliciesInput struct {
|
||||
Policies []RolePerformAddPolicyInput `json:"policies"`
|
||||
}
|
||||
|
||||
type RolePerformAddPolicyInput struct {
|
||||
PolicyId string `json:"policy_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
Ips []string `json:"ips"`
|
||||
}
|
||||
|
||||
type RolePerformRemovePolicyInput struct {
|
||||
PolicyId string `json:"policy_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
}
|
||||
@@ -95,7 +95,6 @@ func InitBaseAuth(options *common_options.BaseOptions) {
|
||||
if options.EnableRbac {
|
||||
policy.EnableGlobalRbac(
|
||||
time.Second*time.Duration(options.RbacPolicySyncPeriodSeconds),
|
||||
time.Second*time.Duration(options.RbacPolicySyncFailedRetrySeconds),
|
||||
options.RbacDebug,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ func optionsEquals(newOpts interface{}, oldOpts interface{}) bool {
|
||||
func (manager *SOptionManager) DoSync(first bool) (time.Duration, error) {
|
||||
newOpts := manager.newOptions()
|
||||
copyOptions(newOpts, manager.options)
|
||||
merged := manager.session.Merge(newOpts, manager.serviceType, manager.serviceVersion)
|
||||
merged := manager.session.Merge(newOpts, manager.serviceType, manager.serviceVersion, first)
|
||||
|
||||
if merged && !optionsEquals(newOpts, manager.options) {
|
||||
if manager.onOptionsChange != nil && manager.onOptionsChange(manager.options, newOpts) && !first {
|
||||
@@ -124,7 +124,7 @@ func (manager *SOptionManager) DoSync(first bool) (time.Duration, error) {
|
||||
appsrv.SetExitFlag()
|
||||
}
|
||||
copyOptions(manager.options, newOpts)
|
||||
manager.session.Upload()
|
||||
manager.session.Upload(first)
|
||||
}
|
||||
return manager.refreshInterval, nil
|
||||
}
|
||||
|
||||
@@ -57,8 +57,8 @@ func getServiceConfig(s *mcclient.ClientSession, serviceId string) (jsonutils.JS
|
||||
}
|
||||
|
||||
type IServiceConfigSession interface {
|
||||
Merge(opts interface{}, serviceType string, serviceVersion string) bool
|
||||
Upload()
|
||||
Merge(opts interface{}, serviceType string, serviceVersion string, isFirst bool) bool
|
||||
Upload(isFirst bool)
|
||||
IsRemote() bool
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func newServiceConfigSession() IServiceConfigSession {
|
||||
return &mcclientServiceConfigSession{}
|
||||
}
|
||||
|
||||
func (s *mcclientServiceConfigSession) Merge(opts interface{}, serviceType string, serviceVersion string) bool {
|
||||
func (s *mcclientServiceConfigSession) Merge(opts interface{}, serviceType string, serviceVersion string, isFirst bool) bool {
|
||||
merged := false
|
||||
s.config = jsonutils.Marshal(opts).(*jsonutils.JSONDict)
|
||||
region, _ := s.config.GetString("region")
|
||||
@@ -88,7 +88,7 @@ func (s *mcclientServiceConfigSession) Merge(opts interface{}, serviceType strin
|
||||
merged = true
|
||||
} else {
|
||||
// not initialized
|
||||
s.Upload()
|
||||
s.Upload(isFirst)
|
||||
}
|
||||
}
|
||||
commonServiceId, _ := getServiceIdByType(s.session, consts.COMMON_SERVICE, "")
|
||||
@@ -113,7 +113,7 @@ func (s *mcclientServiceConfigSession) Merge(opts interface{}, serviceType strin
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *mcclientServiceConfigSession) Upload() {
|
||||
func (s *mcclientServiceConfigSession) Upload(isFirst bool) {
|
||||
// upload service config
|
||||
if len(s.serviceId) > 0 {
|
||||
nconf := jsonutils.NewDict()
|
||||
|
||||
@@ -68,10 +68,10 @@ type BaseOptions struct {
|
||||
NotifyAdminUsers []string `default:"sysadmin" help:"System administrator user ID or name to notify system events, if domain is not default, specify domain as prefix ending with double backslash, e.g. domain\\\\user"`
|
||||
NotifyAdminGroups []string `help:"System administrator group ID or name to notify system events, if domain is not default, specify domain as prefix ending with double backslash, e.g. domain\\\\group"`
|
||||
|
||||
EnableRbac bool `help:"Switch on Role-based Access Control" default:"true"`
|
||||
RbacDebug bool `help:"turn on rbac debug log" default:"false"`
|
||||
RbacPolicySyncPeriodSeconds int `help:"policy sync interval in seconds, default 30 minutes" default:"1800"`
|
||||
RbacPolicySyncFailedRetrySeconds int `help:"seconds to wait after a failed sync, default 30 seconds" default:"30"`
|
||||
EnableRbac bool `help:"Switch on Role-based Access Control" default:"true"`
|
||||
RbacDebug bool `help:"turn on rbac debug log" default:"false"`
|
||||
RbacPolicySyncPeriodSeconds int `help:"policy sync interval in seconds, default 30 minutes" default:"1800"`
|
||||
// RbacPolicySyncFailedRetrySeconds int `help:"seconds to wait after a failed sync, default 30 seconds" default:"30"`
|
||||
|
||||
ConfigSyncPeriodSeconds int `help:"service config sync interval in seconds, default 30 minutes" default:"1800"`
|
||||
|
||||
|
||||
@@ -20,12 +20,12 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
||||
)
|
||||
|
||||
func EnableGlobalRbac(refreshInterval time.Duration, retryInterval time.Duration, debug bool) {
|
||||
func EnableGlobalRbac(refreshInterval time.Duration, debug bool) {
|
||||
if !consts.IsRbacEnabled() {
|
||||
consts.EnableRbac()
|
||||
if debug {
|
||||
consts.EnableRbacDebug()
|
||||
}
|
||||
PolicyManager.start(refreshInterval, retryInterval)
|
||||
PolicyManager.init(refreshInterval)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,16 +25,17 @@ import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/gotypes"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/syncman/watcher"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/util/hashcache"
|
||||
"yunion.io/x/onecloud/pkg/util/nopanic"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
)
|
||||
|
||||
@@ -50,7 +51,7 @@ const (
|
||||
PolicyActionPerform = rbacutils.ActionPerform
|
||||
)
|
||||
|
||||
type PolicyFetchFunc func(ctx context.Context) (map[rbacutils.TRbacScope][]rbacutils.SPolicyInfo, error)
|
||||
type PolicyFetchFunc func(ctx context.Context, token mcclient.TokenCredential) (*mcclient.SFetchMatchPoliciesOutput, error)
|
||||
|
||||
var (
|
||||
PolicyManager *SPolicyManager
|
||||
@@ -61,35 +62,39 @@ func init() {
|
||||
PolicyManager = &SPolicyManager{
|
||||
lock: &sync.Mutex{},
|
||||
}
|
||||
DefaultPolicyFetcher = remotePolicyFetcher
|
||||
DefaultPolicyFetcher = auth.FetchMatchPolicies
|
||||
}
|
||||
|
||||
type SPolicyManager struct {
|
||||
watcher.SInformerSyncManager
|
||||
|
||||
// policies map[rbacutils.TRbacScope]map[string]*rbacutils.SRbacPolicy
|
||||
policies map[rbacutils.TRbacScope][]rbacutils.SPolicyInfo
|
||||
defaultPolicies map[rbacutils.TRbacScope][]*rbacutils.SRbacPolicy
|
||||
|
||||
failedRetryInterval time.Duration
|
||||
refreshInterval time.Duration
|
||||
refreshInterval time.Duration
|
||||
|
||||
cache *hashcache.Cache // policy cache
|
||||
policyCache *hashcache.Cache // policy cache
|
||||
permissionCache *hashcache.Cache // permission cache
|
||||
|
||||
fetchWorker *appsrv.SWorkerManager
|
||||
|
||||
lock *sync.Mutex
|
||||
}
|
||||
|
||||
type sPolicyData struct {
|
||||
Id string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
DomainId string `json:"domain_id"`
|
||||
IsPublic bool `json:"is_public"`
|
||||
PublicScope string `json:"public_scope"`
|
||||
PublicScope rbacutils.TRbacScope `json:"public_scope"`
|
||||
SharedDomains []apis.SharedDomain `json:"shared_domain"`
|
||||
Scope rbacutils.TRbacScope `json:"scope"`
|
||||
Policy jsonutils.JSONObject `json:"policy"`
|
||||
}
|
||||
|
||||
func (data sPolicyData) getPolicy() (rbacutils.TPolicy, error) {
|
||||
return rbacutils.DecodePolicy(data.Policy)
|
||||
}
|
||||
|
||||
/*
|
||||
func parseJsonPolicy(obj jsonutils.JSONObject, enabled bool) (rbacutils.SPolicyInfo, error) {
|
||||
sp := rbacutils.SPolicyInfo{}
|
||||
pData := sPolicyData{}
|
||||
@@ -100,35 +105,33 @@ func parseJsonPolicy(obj jsonutils.JSONObject, enabled bool) (rbacutils.SPolicyI
|
||||
if enabled && !pData.Enabled {
|
||||
return sp, errors.Wrap(httperrors.ErrInvalidFormat, "not enabled")
|
||||
}
|
||||
if len(pData.Type) == 0 {
|
||||
return sp, errors.Wrap(httperrors.ErrInvalidFormat, "missing type")
|
||||
if len(pData.Name) == 0 {
|
||||
return sp, errors.Wrap(httperrors.ErrInvalidFormat, "missing name")
|
||||
}
|
||||
|
||||
if pData.Policy == nil {
|
||||
return sp, errors.Wrap(httperrors.ErrInvalidFormat, "missing policy")
|
||||
}
|
||||
|
||||
policy := rbacutils.SRbacPolicy{}
|
||||
policy := rbacutils.SRbacPolicyCore{}
|
||||
err = policy.Decode(pData.Policy)
|
||||
if err != nil {
|
||||
log.Errorf("policy decode error %s", err)
|
||||
return sp, errors.Wrap(err, "policy.Decode")
|
||||
}
|
||||
|
||||
policy.DomainId = pData.DomainId
|
||||
policy.IsPublic = pData.IsPublic
|
||||
policy.PublicScope = rbacutils.String2ScopeDefault(pData.PublicScope, rbacutils.ScopeSystem)
|
||||
policy.SharedDomainIds = make([]string, len(pData.SharedDomains))
|
||||
sp.SharedDomainIds = make([]string, len(pData.SharedDomains))
|
||||
for i := range pData.SharedDomains {
|
||||
policy.SharedDomainIds[i] = pData.SharedDomains[i].Id
|
||||
sp.SharedDomainIds[i] = pData.SharedDomains[i].Id
|
||||
}
|
||||
|
||||
sp.Id = pData.Id
|
||||
sp.Name = pData.Type
|
||||
sp.Name = pData.Name
|
||||
sp.Policy = &policy
|
||||
return sp, nil
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
func remotePolicyFetcher(ctx context.Context) (map[rbacutils.TRbacScope][]rbacutils.SPolicyInfo, error) {
|
||||
s := auth.GetAdminSession(ctx, consts.GetRegion(), "v1")
|
||||
|
||||
@@ -153,10 +156,10 @@ func remotePolicyFetcher(ctx context.Context) (map[rbacutils.TRbacScope][]rbacut
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := policies[sp.Policy.Scope]; !ok {
|
||||
policies[sp.Policy.Scope] = make([]rbacutils.SPolicyInfo, 0)
|
||||
if _, ok := policies[sp.Scope]; !ok {
|
||||
policies[sp.Scope] = make([]rbacutils.SPolicyInfo, 0)
|
||||
}
|
||||
policies[sp.Policy.Scope] = append(policies[sp.Policy.Scope], sp)
|
||||
policies[sp.Scope] = append(policies[sp.Scope], sp)
|
||||
}
|
||||
|
||||
offset += len(result.Data)
|
||||
@@ -166,12 +169,11 @@ func remotePolicyFetcher(ctx context.Context) (map[rbacutils.TRbacScope][]rbacut
|
||||
}
|
||||
return policies, nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (manager *SPolicyManager) start(refreshInterval time.Duration, retryInterval time.Duration) {
|
||||
log.Infof("PolicyManager start to fetch policies ...")
|
||||
manager.failedRetryInterval = retryInterval
|
||||
func (manager *SPolicyManager) init(refreshInterval time.Duration) {
|
||||
manager.refreshInterval = refreshInterval
|
||||
manager.InitSync(manager)
|
||||
// manager.InitSync(manager)
|
||||
if len(predefinedDefaultPolicies) > 0 {
|
||||
policiesMap := make(map[rbacutils.TRbacScope][]*rbacutils.SRbacPolicy)
|
||||
for i := range predefinedDefaultPolicies {
|
||||
@@ -184,25 +186,26 @@ func (manager *SPolicyManager) start(refreshInterval time.Duration, retryInterva
|
||||
policiesMap[policy.Scope] = policies
|
||||
}
|
||||
manager.defaultPolicies = policiesMap
|
||||
// log.Debugf("%#v", manager.defaultPolicies)
|
||||
}
|
||||
|
||||
manager.cache = hashcache.NewCache(2048, refreshInterval/2)
|
||||
manager.policyCache = hashcache.NewCache(2048, refreshInterval)
|
||||
manager.permissionCache = hashcache.NewCache(2048, refreshInterval)
|
||||
|
||||
defaultFetcherFuncAddr := reflect.ValueOf(DefaultPolicyFetcher).Pointer()
|
||||
remoteFetcherFuncAddr := reflect.ValueOf(remotePolicyFetcher).Pointer()
|
||||
remoteFetcherFuncAddr := reflect.ValueOf(auth.FetchMatchPolicies).Pointer()
|
||||
log.Debugf("DefaultPolicyFetcher: %x RemotePolicyFetcher: %x", defaultFetcherFuncAddr, remoteFetcherFuncAddr)
|
||||
var isDB bool
|
||||
if defaultFetcherFuncAddr == remoteFetcherFuncAddr {
|
||||
// remote fetcher, so start watcher
|
||||
manager.StartWatching(&modules.Policies)
|
||||
isDB = false
|
||||
} else {
|
||||
isDB = true
|
||||
}
|
||||
|
||||
err := manager.FirstSync()
|
||||
if err != nil {
|
||||
log.Errorf("PolicyManager first sync fail %s", err)
|
||||
}
|
||||
manager.fetchWorker = appsrv.NewWorkerManager("policyFetchWorker", 1, 2048, isDB)
|
||||
}
|
||||
|
||||
/*
|
||||
func (manager *SPolicyManager) DoSync(first bool) (time.Duration, error) {
|
||||
var err error
|
||||
|
||||
@@ -236,15 +239,33 @@ func (manager *SPolicyManager) NeedSync(dat *jsonutils.JSONDict) bool {
|
||||
func (manager *SPolicyManager) Name() string {
|
||||
return "PolicyManager"
|
||||
}
|
||||
*/
|
||||
|
||||
func queryKey(scope rbacutils.TRbacScope, userCred mcclient.TokenCredential, service string, resource string, action string, extra ...string) string {
|
||||
func getMaskedLoginIp(userCred mcclient.TokenCredential) string {
|
||||
loginIp, _ := netutils.NewIPV4Addr(userCred.GetLoginIp())
|
||||
return loginIp.NetAddr(16).String()
|
||||
}
|
||||
|
||||
func policyKey(userCred mcclient.TokenCredential) string {
|
||||
keys := []string{userCred.GetProjectId()}
|
||||
roles := userCred.GetRoleIds()
|
||||
if len(roles) > 0 {
|
||||
sort.Strings(roles)
|
||||
}
|
||||
keys = append(keys, strings.Join(roles, ":"))
|
||||
keys = append(keys, getMaskedLoginIp(userCred))
|
||||
return strings.Join(keys, "-")
|
||||
}
|
||||
|
||||
func permissionKey(scope rbacutils.TRbacScope, userCred mcclient.TokenCredential, service string, resource string, action string, extra ...string) string {
|
||||
queryKeys := []string{string(scope)}
|
||||
queryKeys = append(queryKeys, userCred.GetProjectId(), userCred.GetDomainId(), userCred.GetUserId())
|
||||
roles := userCred.GetRoles()
|
||||
queryKeys = append(queryKeys, userCred.GetProjectId())
|
||||
roles := userCred.GetRoleIds()
|
||||
if len(roles) > 0 {
|
||||
sort.Strings(roles)
|
||||
}
|
||||
queryKeys = append(queryKeys, strings.Join(roles, ":"))
|
||||
queryKeys = append(queryKeys, getMaskedLoginIp(userCred))
|
||||
if rbacutils.WILD_MATCH == service || len(service) == 0 {
|
||||
service = rbacutils.WILD_MATCH
|
||||
}
|
||||
@@ -311,25 +332,63 @@ func (manager *SPolicyManager) Allow(targetScope rbacutils.TRbacScope, userCred
|
||||
return rbacutils.Deny
|
||||
}
|
||||
|
||||
func (manager *SPolicyManager) allow(scope rbacutils.TRbacScope, userCred mcclient.TokenCredential, service string, resource string, action string, extra ...string) rbacutils.TRbacResult {
|
||||
if manager.cache != nil && userCred != nil {
|
||||
key := queryKey(scope, userCred, service, resource, action, extra...)
|
||||
val := manager.cache.Get(key)
|
||||
if val != nil {
|
||||
if consts.IsRbacDebug() {
|
||||
log.Debugf("query %s:%s:%s:%s from cache %s", service, resource, action, extra, val)
|
||||
}
|
||||
return val.(rbacutils.TRbacResult)
|
||||
}
|
||||
result := manager.allowWithoutCache(scope, userCred, service, resource, action, extra...)
|
||||
manager.cache.Set(key, result)
|
||||
return result
|
||||
} else {
|
||||
return manager.allowWithoutCache(scope, userCred, service, resource, action, extra...)
|
||||
func (manager *SPolicyManager) fetchMatchedPolicies(userCred mcclient.TokenCredential) (*mcclient.SFetchMatchPoliciesOutput, error) {
|
||||
key := policyKey(userCred)
|
||||
|
||||
type fetchResult struct {
|
||||
output *mcclient.SFetchMatchPoliciesOutput
|
||||
err error
|
||||
}
|
||||
resChan := make(chan fetchResult)
|
||||
manager.fetchWorker.Run(func() {
|
||||
val := manager.policyCache.Get(key)
|
||||
result := fetchResult{}
|
||||
if gotypes.IsNil(val) {
|
||||
pg, err := DefaultPolicyFetcher(context.Background(), userCred)
|
||||
if err != nil {
|
||||
result.err = errors.Wrap(err, "DefaultPolicyFetcher")
|
||||
} else {
|
||||
manager.policyCache.Set(key, pg)
|
||||
result.output = pg
|
||||
}
|
||||
} else {
|
||||
result.output = val.(*mcclient.SFetchMatchPoliciesOutput)
|
||||
}
|
||||
resChan <- result
|
||||
}, nil, nil)
|
||||
|
||||
res := <-resChan
|
||||
return res.output, res.err
|
||||
}
|
||||
|
||||
func (manager *SPolicyManager) findPolicyByName(scope rbacutils.TRbacScope, name string) *rbacutils.SRbacPolicy {
|
||||
func (manager *SPolicyManager) allow(scope rbacutils.TRbacScope, userCred mcclient.TokenCredential, service string, resource string, action string, extra ...string) rbacutils.TRbacResult {
|
||||
// first download userCred policy
|
||||
policies, err := manager.fetchMatchedPolicies(userCred)
|
||||
if err != nil {
|
||||
log.Errorf("fetchMatchedPolicyGroup fail %s", err)
|
||||
return rbacutils.Deny
|
||||
}
|
||||
// check permission
|
||||
key := permissionKey(scope, userCred, service, resource, action, extra...)
|
||||
val := manager.permissionCache.AtomicGet(key)
|
||||
if !gotypes.IsNil(val) {
|
||||
if consts.IsRbacDebug() {
|
||||
log.Debugf("query %s:%s:%s:%s from cache %s", service, resource, action, extra, val)
|
||||
}
|
||||
return val.(rbacutils.TRbacResult)
|
||||
}
|
||||
|
||||
policySet, ok := policies.Policies[scope]
|
||||
if !ok {
|
||||
policySet = rbacutils.TPolicySet{}
|
||||
}
|
||||
result := manager.allowWithoutCache(policySet, scope, userCred, service, resource, action, extra...)
|
||||
manager.permissionCache.Set(key, result)
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
func (manager *SPolicyManager) findPolicyByName(scope rbacutils.TRbacScope, name string) *rbacutils.SRbacPolicyCore {
|
||||
if policies, ok := manager.policies[scope]; ok {
|
||||
for i := range policies {
|
||||
if policies[i].Id == name || policies[i].Name == name {
|
||||
@@ -352,15 +411,15 @@ func getMatchedPolicyRules(policies []rbacutils.SPolicyInfo, userCred rbacutils.
|
||||
}
|
||||
return matchPolicies.GetMatchRules(service, resource, action, extra...), true
|
||||
}
|
||||
*/
|
||||
|
||||
func (manager *SPolicyManager) allowWithoutCache(scope rbacutils.TRbacScope, userCred mcclient.TokenCredential, service string, resource string, action string, extra ...string) rbacutils.TRbacResult {
|
||||
func (manager *SPolicyManager) allowWithoutCache(policies rbacutils.TPolicySet, scope rbacutils.TRbacScope, userCred mcclient.TokenCredential, service string, resource string, action string, extra ...string) rbacutils.TRbacResult {
|
||||
matchRules := make([]rbacutils.SRbacRule, 0)
|
||||
findMatchPolicy := false
|
||||
policies, ok := manager.policies[scope]
|
||||
if !ok {
|
||||
if len(policies) == 0 {
|
||||
log.Warningf("no policies fetched for scope %s", scope)
|
||||
} else {
|
||||
matchRules, findMatchPolicy = getMatchedPolicyRules(policies, userCred, service, resource, action, extra...)
|
||||
matchRules = policies.GetMatchRules(service, resource, action, extra...)
|
||||
}
|
||||
|
||||
scopedDeny := false
|
||||
@@ -397,7 +456,7 @@ func (manager *SPolicyManager) allowWithoutCache(scope rbacutils.TRbacScope, use
|
||||
if !isMatched {
|
||||
continue
|
||||
}
|
||||
rule := defaultPolicies[i].GetMatchRule(service, resource, action, extra...)
|
||||
rule := defaultPolicies[i].Rules.GetMatchRule(service, resource, action, extra...)
|
||||
if rule != nil {
|
||||
matchRules = append(matchRules, *rule)
|
||||
}
|
||||
@@ -432,21 +491,26 @@ func (manager *SPolicyManager) allowWithoutCache(scope rbacutils.TRbacScope, use
|
||||
// result: allow/deny for the named policy
|
||||
// userResult: allow/deny for the matched policies of userCred
|
||||
//
|
||||
func explainPolicy(ctx context.Context, userCred mcclient.TokenCredential, policyReq jsonutils.JSONObject, name string) ([]string, rbacutils.TRbacResult, rbacutils.TRbacResult, error) {
|
||||
_, request, result, userResult, err := explainPolicyInternal(ctx, userCred, policyReq, name)
|
||||
func explainPolicy(userCred mcclient.TokenCredential, policyReq jsonutils.JSONObject, policyData *sPolicyData) ([]string, rbacutils.TRbacResult, rbacutils.TRbacResult, error) {
|
||||
_, request, result, userResult, err := explainPolicyInternal(userCred, policyReq, policyData)
|
||||
return request, result, userResult, err
|
||||
}
|
||||
|
||||
func fetchPolicyByIdOrName(ctx context.Context, id string) (rbacutils.SPolicyInfo, error) {
|
||||
func fetchPolicyDataByIdOrName(ctx context.Context, id string) (*sPolicyData, error) {
|
||||
s := auth.GetAdminSession(ctx, consts.GetRegion(), "v1")
|
||||
data, err := modules.Policies.Get(s, id, nil)
|
||||
if err != nil {
|
||||
return rbacutils.SPolicyInfo{}, errors.Wrap(err, "modules.Policies.Get")
|
||||
return nil, errors.Wrap(err, "modules.Policies.Get")
|
||||
}
|
||||
return parseJsonPolicy(data, false)
|
||||
pdata := &sPolicyData{}
|
||||
err = data.Unmarshal(&pdata)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Unmarshal Policy Data")
|
||||
}
|
||||
return pdata, nil
|
||||
}
|
||||
|
||||
func explainPolicyInternal(ctx context.Context, userCred mcclient.TokenCredential, policyReq jsonutils.JSONObject, name string) (rbacutils.TRbacScope, []string, rbacutils.TRbacResult, rbacutils.TRbacResult, error) {
|
||||
func explainPolicyInternal(userCred mcclient.TokenCredential, policyReq jsonutils.JSONObject, policyData *sPolicyData) (rbacutils.TRbacScope, []string, rbacutils.TRbacResult, rbacutils.TRbacResult, error) {
|
||||
policySeq, err := policyReq.GetArray()
|
||||
if err != nil {
|
||||
return rbacutils.ScopeSystem, nil, rbacutils.Deny, rbacutils.Deny, httperrors.NewInputParameterError("invalid format")
|
||||
@@ -489,21 +553,19 @@ func explainPolicyInternal(ctx context.Context, userCred mcclient.TokenCredentia
|
||||
userResult := PolicyManager.Allow(scope, userCred, service, resource, action, extra...)
|
||||
result := userResult
|
||||
|
||||
if len(name) > 0 {
|
||||
policy := PolicyManager.findPolicyByName(scope, name)
|
||||
if policy == nil {
|
||||
// policy not found locally, remote fetch
|
||||
sp, err := fetchPolicyByIdOrName(ctx, name)
|
||||
if policyData != nil {
|
||||
if scope.HigherThan(policyData.Scope) {
|
||||
result = rbacutils.Deny
|
||||
} else {
|
||||
policy, err := policyData.getPolicy()
|
||||
if err != nil {
|
||||
return scope, reqStrs, rbacutils.Deny, rbacutils.Deny, httperrors.NewNotFoundError("policy %s not found: %s", name, err)
|
||||
return scope, reqStrs, rbacutils.Deny, userResult, errors.Wrap(err, "getPolicy")
|
||||
}
|
||||
rule := policy.GetMatchRule(service, resource, action, extra...)
|
||||
result = rbacutils.Deny
|
||||
if rule != nil {
|
||||
result = rule.Result
|
||||
}
|
||||
policy = sp.Policy
|
||||
}
|
||||
|
||||
rule := policy.GetMatchRule(service, resource, action, extra...)
|
||||
result = rbacutils.Deny
|
||||
if rule != nil {
|
||||
result = rule.Result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,9 +577,16 @@ func ExplainRpc(ctx context.Context, userCred mcclient.TokenCredential, params j
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInputParameterError("invalid input format")
|
||||
}
|
||||
var policyData *sPolicyData
|
||||
if len(name) > 0 {
|
||||
policyData, err = fetchPolicyDataByIdOrName(ctx, name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetchPolicyDataByIdOrName")
|
||||
}
|
||||
}
|
||||
ret := jsonutils.NewDict()
|
||||
for key, policyReq := range paramDict {
|
||||
reqStrs, result, userResult, err := explainPolicy(ctx, userCred, policyReq, name)
|
||||
reqStrs, result, userResult, err := explainPolicy(userCred, policyReq, policyData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -541,24 +610,36 @@ func (manager *SPolicyManager) IsScopeCapable(userCred mcclient.TokenCredential,
|
||||
return false
|
||||
}
|
||||
|
||||
if policies, ok := manager.policies[scope]; ok {
|
||||
pnames := getMatchedPolicyNames(policies, userCred)
|
||||
if len(pnames) > 0 {
|
||||
return true
|
||||
}
|
||||
policies, err := manager.fetchMatchedPolicies(userCred)
|
||||
if err != nil {
|
||||
log.Errorf("fetchMatchedPolicyGroup fail %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if set, ok := policies.Policies[scope]; ok && len(set) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (manager *SPolicyManager) MatchedPolicyNames(scope rbacutils.TRbacScope, userCred rbacutils.IRbacIdentity) []string {
|
||||
/*
|
||||
func (manager *SPolicyManager) MatchedPolicyNames(ctx context.Context, scope rbacutils.TRbacScope, ident rbacutils.IRbacIdentity) []string {
|
||||
policies, err := manager.fetchMatchedPolicies(ctx, userCred)
|
||||
if err != nil {
|
||||
log.Errorf("fetchMatchedPolicyGroup fail %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
ret := make([]string, 0)
|
||||
policies, ok := manager.policies[scope]
|
||||
if !ok {
|
||||
return ret
|
||||
}
|
||||
return getMatchedPolicyNames(policies, userCred)
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
func (manager *SPolicyManager) AllPolicies() map[string][]string {
|
||||
ret := make(map[string][]string)
|
||||
for scope, p := range manager.policies {
|
||||
@@ -597,3 +678,4 @@ func (manager *SPolicyManager) GetMatchedPolicySet(userCred rbacutils.IRbacIdent
|
||||
}
|
||||
return rbacutils.ScopeNone, nil
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
@@ -589,9 +588,10 @@ func (manager *SAssignmentManager) queryAll(
|
||||
}
|
||||
|
||||
func fetchRoleAssignmentPolicies(ra *api.SRoleAssignment) {
|
||||
ra.Policies.Project = policy.PolicyManager.MatchedPolicyNames(rbacutils.ScopeProject, ra)
|
||||
ra.Policies.Domain = policy.PolicyManager.MatchedPolicyNames(rbacutils.ScopeDomain, ra)
|
||||
ra.Policies.System = policy.PolicyManager.MatchedPolicyNames(rbacutils.ScopeSystem, ra)
|
||||
policyNames, _, _ := RolePolicyManager.GetMatchPolicyGroup(ra, true)
|
||||
ra.Policies.Project, _ = policyNames[rbacutils.ScopeProject]
|
||||
ra.Policies.Domain, _ = policyNames[rbacutils.ScopeDomain]
|
||||
ra.Policies.System, _ = policyNames[rbacutils.ScopeSystem]
|
||||
}
|
||||
|
||||
type sAssignmentInternal struct {
|
||||
|
||||
@@ -367,7 +367,7 @@ func GetConfigs(model db.IModel, sensitive bool, whiteList, blackList map[string
|
||||
return config2map(opts), nil
|
||||
}
|
||||
|
||||
func saveConfigs(userCred mcclient.TokenCredential, action string, model db.IModel, opts api.TConfigs, whiteList map[string][]string, blackList map[string][]string, sensitiveConfs map[string][]string) error {
|
||||
func saveConfigs(userCred mcclient.TokenCredential, action string, model db.IModel, opts api.TConfigs, whiteList map[string][]string, blackList map[string][]string, sensitiveConfs map[string][]string, skipLog bool) error {
|
||||
var err error
|
||||
changed := make([]sChangeOption, 0)
|
||||
changedSensitive := make([]sChangeOption, 0)
|
||||
@@ -417,7 +417,9 @@ func saveConfigs(userCred mcclient.TokenCredential, action string, model db.IMod
|
||||
if len(changed) > 0 {
|
||||
notes := jsonutils.Marshal(changed)
|
||||
db.OpsLog.LogEvent(model, db.ACT_CHANGE_CONFIG, notes, userCred)
|
||||
logclient.AddSimpleActionLog(model, logclient.ACT_CHANGE_CONFIG, notes, userCred, true)
|
||||
if !skipLog {
|
||||
logclient.AddSimpleActionLog(model, logclient.ACT_CHANGE_CONFIG, notes, userCred, true)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -431,7 +433,7 @@ func NewServiceConfigSession() common_options.IServiceConfigSession {
|
||||
return &dbServiceConfigSession{}
|
||||
}
|
||||
|
||||
func (s *dbServiceConfigSession) Merge(opts interface{}, serviceType string, serviceVersion string) bool {
|
||||
func (s *dbServiceConfigSession) Merge(opts interface{}, serviceType string, serviceVersion string, isFirst bool) bool {
|
||||
merged := false
|
||||
s.config = jsonutils.Marshal(opts).(*jsonutils.JSONDict)
|
||||
s.service, _ = ServiceManager.fetchServiceByType(serviceType)
|
||||
@@ -445,7 +447,7 @@ func (s *dbServiceConfigSession) Merge(opts interface{}, serviceType string, ser
|
||||
merged = true
|
||||
} else {
|
||||
// not initialized
|
||||
uploadConfig(s.service, s.config)
|
||||
uploadConfig(s.service, s.config, isFirst)
|
||||
}
|
||||
}
|
||||
commonService, _ := ServiceManager.fetchServiceByType(consts.COMMON_SERVICE)
|
||||
@@ -459,7 +461,7 @@ func (s *dbServiceConfigSession) Merge(opts interface{}, serviceType string, ser
|
||||
merged = true
|
||||
} else {
|
||||
// common not initialized
|
||||
uploadConfig(commonService, s.config)
|
||||
uploadConfig(commonService, s.config, isFirst)
|
||||
}
|
||||
}
|
||||
if merged {
|
||||
@@ -472,18 +474,18 @@ func (s *dbServiceConfigSession) Merge(opts interface{}, serviceType string, ser
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *dbServiceConfigSession) Upload() {
|
||||
func (s *dbServiceConfigSession) Upload(isFirst bool) {
|
||||
if s.service == nil {
|
||||
return
|
||||
}
|
||||
uploadConfig(s.service, s.config)
|
||||
uploadConfig(s.service, s.config, isFirst)
|
||||
}
|
||||
|
||||
func (s *dbServiceConfigSession) IsRemote() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func uploadConfig(service *SService, config jsonutils.JSONObject) {
|
||||
func uploadConfig(service *SService, config jsonutils.JSONObject, isFirst bool) {
|
||||
nconf := jsonutils.NewDict()
|
||||
nconf.Add(config, "default")
|
||||
tconf := api.TConfigs{}
|
||||
@@ -493,9 +495,9 @@ func uploadConfig(service *SService, config jsonutils.JSONObject) {
|
||||
return
|
||||
}
|
||||
if service.isCommonService() {
|
||||
err = saveConfigs(nil, "", service, tconf, api.CommonWhitelistOptionMap, nil, nil)
|
||||
err = saveConfigs(nil, "", service, tconf, api.CommonWhitelistOptionMap, nil, nil, isFirst)
|
||||
} else {
|
||||
err = saveConfigs(nil, "", service, tconf, nil, api.ServiceBlacklistOptionMap, nil)
|
||||
err = saveConfigs(nil, "", service, tconf, nil, api.ServiceBlacklistOptionMap, nil, isFirst)
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("saveConfigs fail %s", err)
|
||||
|
||||
@@ -65,7 +65,9 @@ func getDefaultAdminCred() mcclient.TokenCredential {
|
||||
token.Project = prj.Name
|
||||
token.ProjectDomainId = prj.DomainId
|
||||
token.ProjectDomain = prj.GetDomain().Name
|
||||
token.Roles = api.SystemAdminRole
|
||||
rol, _ := RoleManager.FetchRole("", api.SystemAdminRole, api.DEFAULT_DOMAIN_ID, "")
|
||||
token.Roles = rol.Name
|
||||
token.RoleIds = rol.Id
|
||||
return &token
|
||||
}
|
||||
|
||||
|
||||
@@ -340,7 +340,7 @@ func (ident *SIdentityProvider) PerformConfig(ctx context.Context, userCred mccl
|
||||
|
||||
opts := input.Config
|
||||
action := input.Action
|
||||
err = saveConfigs(userCred, action, ident, opts, nil, nil, api.SensitiveDomainConfigMap)
|
||||
err = saveConfigs(userCred, action, ident, opts, nil, nil, api.SensitiveDomainConfigMap, false)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInternalServerError("saveConfig fail %s", err)
|
||||
}
|
||||
@@ -492,7 +492,7 @@ func (ident *SIdentityProvider) PostCreate(ctx context.Context, userCred mcclien
|
||||
log.Errorf("parse config error %s", err)
|
||||
return
|
||||
}
|
||||
err = saveConfigs(userCred, "", ident, opts, nil, nil, api.SensitiveDomainConfigMap)
|
||||
err = saveConfigs(userCred, "", ident, opts, nil, nil, api.SensitiveDomainConfigMap, false)
|
||||
if err != nil {
|
||||
log.Errorf("saveConfig fail %s", err)
|
||||
return
|
||||
|
||||
+225
-19
@@ -22,6 +22,7 @@ import (
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/gotypes"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
@@ -69,8 +70,18 @@ type SPolicy struct {
|
||||
SEnabledIdentityBaseResource
|
||||
db.SSharableBaseResource `"is_public=>create":"domain_optional" "public_scope=>create":"domain_optional"`
|
||||
|
||||
Type string `width:"255" charset:"utf8" nullable:"false" list:"user" create:"domain_required" update:"domain"`
|
||||
// swagger:ignore
|
||||
// Deprecated
|
||||
Type string `width:"255" charset:"utf8" nullable:"false" list:"user" create:"domain_required" update:"domain"`
|
||||
|
||||
// 权限定义
|
||||
Blob jsonutils.JSONObject `nullable:"false" list:"user" create:"domain_required" update:"domain"`
|
||||
|
||||
// 权限范围
|
||||
Scope rbacutils.TRbacScope `nullable:"true" list:"user" create:"domain_required" update:"domain"`
|
||||
|
||||
// 是否为系统权限
|
||||
IsSystem tristate.TriState `nullable:"false" default:"false" list:"domain" update:"admin" create:"admin_optional"`
|
||||
}
|
||||
|
||||
func (manager *SPolicyManager) InitializeData() error {
|
||||
@@ -91,6 +102,113 @@ func (manager *SPolicyManager) InitializeData() error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
err = manager.initializeRolePolicyGroup()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (manager *SPolicyManager) initializeRolePolicyGroup() error {
|
||||
ctx := context.Background()
|
||||
q := manager.Query()
|
||||
q = q.IsNullOrEmpty("scope")
|
||||
policies := make([]SPolicy, 0)
|
||||
err := db.FetchModelObjects(manager, q, &policies)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range policies {
|
||||
var policy rbacutils.SRbacPolicy
|
||||
err := policy.Decode(policies[i].Blob)
|
||||
if err != nil {
|
||||
log.Errorf("Decode policy %s failed %s", policies[i].Name, err)
|
||||
continue
|
||||
}
|
||||
failed := false
|
||||
if len(policy.Roles) == 0 && len(policy.Projects) == 0 {
|
||||
// match any
|
||||
roles, err := policies[i].fetchMatchableRoles()
|
||||
if err != nil {
|
||||
log.Errorf("policy fetchMatchableRoles fail %s", err)
|
||||
failed = true
|
||||
} else {
|
||||
for _, r := range roles {
|
||||
err = RolePolicyManager.newRecord(ctx, r.Id, "", policies[i].Id, tristate.NewFromBool(policy.Auth), policy.Ips)
|
||||
if err != nil {
|
||||
log.Errorf("insert role policy fail %s", err)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if len(policy.Roles) > 0 && len(policy.Projects) == 0 {
|
||||
for _, r := range policy.Roles {
|
||||
role, err := RoleManager.FetchRoleByName(r, policies[i].DomainId, "")
|
||||
if err != nil {
|
||||
log.Errorf("fetch role %s fail %s", r, err)
|
||||
continue
|
||||
}
|
||||
err = RolePolicyManager.newRecord(ctx, role.Id, "", policies[i].Id, tristate.True, policy.Ips)
|
||||
if err != nil {
|
||||
log.Errorf("insert role policy fail %s", err)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
} else if len(policy.Roles) == 0 && len(policy.Projects) > 0 {
|
||||
for _, p := range policy.Projects {
|
||||
project, err := ProjectManager.FetchProjectByName(p, policies[i].DomainId, "")
|
||||
if err != nil {
|
||||
log.Errorf("fetch porject %s fail %s", p, err)
|
||||
continue
|
||||
}
|
||||
roles, err := policies[i].fetchMatchableRoles()
|
||||
if err != nil {
|
||||
log.Errorf("policy fetchMatchableRoles fail %s", err)
|
||||
failed = true
|
||||
} else {
|
||||
for _, r := range roles {
|
||||
err = RolePolicyManager.newRecord(ctx, r.Id, project.Id, policies[i].Id, tristate.True, policy.Ips)
|
||||
if err != nil {
|
||||
log.Errorf("insert role policy fail %s", err)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if len(policy.Roles) > 0 && len(policy.Projects) > 0 {
|
||||
for _, r := range policy.Roles {
|
||||
role, err := RoleManager.FetchRoleByName(r, policies[i].DomainId, "")
|
||||
if err != nil {
|
||||
log.Errorf("fetch role %s fail %s", r, err)
|
||||
continue
|
||||
}
|
||||
for _, p := range policy.Projects {
|
||||
project, err := ProjectManager.FetchProjectByName(p, policies[i].DomainId, "")
|
||||
if err != nil {
|
||||
log.Errorf("fetch project %s fail %s", p, err)
|
||||
continue
|
||||
}
|
||||
err = RolePolicyManager.newRecord(ctx, role.Id, project.Id, policies[i].Id, tristate.True, policy.Ips)
|
||||
if err != nil {
|
||||
log.Errorf("insert role policy fail %s", err)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !failed {
|
||||
db.Update(&policies[i], func() error {
|
||||
policies[i].Scope = policy.Scope
|
||||
// do not rewrite blob, make backward compatible
|
||||
// policies[i].Blob = policy.Rules.Encode()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -106,16 +224,34 @@ func (manager *SPolicyManager) FetchEnabledPolicies() ([]SPolicy, error) {
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
func validatePolicyVioldatePrivilege(userCred mcclient.TokenCredential, policy *rbacutils.SRbacPolicy) error {
|
||||
func validatePolicyVioldatePrivilege(userCred mcclient.TokenCredential, policyScope rbacutils.TRbacScope, policy rbacutils.TPolicy) error {
|
||||
if userCred.GetUserName() == api.SystemAdminUser && userCred.GetDomainId() == api.DEFAULT_DOMAIN_ID {
|
||||
return nil
|
||||
}
|
||||
opsScope, opsPolicySet := policyman.PolicyManager.GetMatchedPolicySet(userCred)
|
||||
if opsScope != rbacutils.ScopeSystem && policy.Scope.HigherThan(opsScope) {
|
||||
return errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "cannot create policy scope higher than %s", opsScope)
|
||||
_, policyGroup, err := RolePolicyManager.GetMatchPolicyGroup(userCred, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetMatchPolicyGroup")
|
||||
}
|
||||
noViolate := false
|
||||
assignPolicySet := rbacutils.TPolicySet{policy}
|
||||
if !opsScope.HigherThan(policy.Scope) && opsPolicySet.ViolatedBy(assignPolicySet) {
|
||||
for _, scope := range []rbacutils.TRbacScope{
|
||||
rbacutils.ScopeSystem,
|
||||
rbacutils.ScopeDomain,
|
||||
rbacutils.ScopeProject,
|
||||
} {
|
||||
isViolate := false
|
||||
policySet, ok := policyGroup[scope]
|
||||
if !ok || len(policySet) == 0 || policySet.ViolatedBy(assignPolicySet) {
|
||||
isViolate = true
|
||||
}
|
||||
if !isViolate {
|
||||
noViolate = true
|
||||
}
|
||||
if scope == policyScope {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !noViolate {
|
||||
return errors.Wrap(httperrors.ErrNotSufficientPrivilege, "policy violates operator's policy")
|
||||
}
|
||||
return nil
|
||||
@@ -129,17 +265,21 @@ func (manager *SPolicyManager) ValidateCreateData(
|
||||
input api.PolicyCreateInput,
|
||||
) (api.PolicyCreateInput, error) {
|
||||
var err error
|
||||
if len(input.Type) == 0 {
|
||||
if len(input.Type) == 0 && len(input.Name) == 0 {
|
||||
return input, httperrors.NewInputParameterError("missing input field type")
|
||||
}
|
||||
input.Name = input.Type
|
||||
policy := rbacutils.SRbacPolicy{}
|
||||
err = policy.Decode(input.Blob)
|
||||
if len(input.Name) == 0 {
|
||||
input.Name = input.Type
|
||||
}
|
||||
|
||||
policy, err := rbacutils.DecodePolicyData(input.Blob)
|
||||
if err != nil {
|
||||
return input, httperrors.NewInputParameterError("fail to decode policy data")
|
||||
}
|
||||
|
||||
err = validatePolicyVioldatePrivilege(userCred, &policy)
|
||||
input.Scope = rbacutils.String2ScopeDefault(string(input.Scope), rbacutils.ScopeProject)
|
||||
|
||||
err = validatePolicyVioldatePrivilege(userCred, input.Scope, policy)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "validatePolicyVioldatePrivilege")
|
||||
}
|
||||
@@ -166,20 +306,49 @@ func (manager *SPolicyManager) ValidateCreateData(
|
||||
return input, errors.Wrap(err, "CheckSetPendingQuota")
|
||||
}
|
||||
|
||||
requireScope := input.Scope
|
||||
if input.IsSystem != nil && *input.IsSystem {
|
||||
requireScope = rbacutils.ScopeSystem
|
||||
}
|
||||
allowScope := policyman.PolicyManager.AllowScope(userCred, api.SERVICE_TYPE, manager.KeywordPlural(), policyman.PolicyActionCreate)
|
||||
if requireScope.HigherThan(allowScope) {
|
||||
return input, errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", requireScope, allowScope)
|
||||
}
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (policy *SPolicy) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.PolicyUpdateInput) (api.PolicyUpdateInput, error) {
|
||||
var requireScope rbacutils.TRbacScope
|
||||
|
||||
if len(input.Scope) > 0 {
|
||||
input.Scope = rbacutils.String2ScopeDefault(string(input.Scope), rbacutils.ScopeProject)
|
||||
requireScope = input.Scope
|
||||
}
|
||||
if input.IsSystem != nil && *input.IsSystem {
|
||||
requireScope = rbacutils.ScopeSystem
|
||||
}
|
||||
|
||||
if len(requireScope) > 0 {
|
||||
allowScope := policyman.PolicyManager.AllowScope(userCred, api.SERVICE_TYPE, policy.KeywordPlural(), policyman.PolicyActionUpdate)
|
||||
if requireScope.HigherThan(allowScope) {
|
||||
return input, errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", requireScope, allowScope)
|
||||
}
|
||||
}
|
||||
|
||||
if input.Blob != nil {
|
||||
p := rbacutils.SRbacPolicy{}
|
||||
err := p.Decode(input.Blob)
|
||||
p, err := rbacutils.DecodePolicyData(input.Blob)
|
||||
if err != nil {
|
||||
return input, 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")
|
||||
} */
|
||||
err = validatePolicyVioldatePrivilege(userCred, &p)
|
||||
scope := input.Scope
|
||||
if len(scope) == 0 {
|
||||
scope = policy.Scope
|
||||
}
|
||||
err = validatePolicyVioldatePrivilege(userCred, scope, p)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "validatePolicyVioldatePrivilege")
|
||||
}
|
||||
@@ -192,6 +361,7 @@ func (policy *SPolicy) ValidateUpdateData(ctx context.Context, userCred mcclient
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "SEnabledIdentityBaseResource.ValidateUpdateData")
|
||||
}
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
@@ -207,17 +377,17 @@ func (policy *SPolicy) PostCreate(ctx context.Context, userCred mcclient.TokenCr
|
||||
log.Errorf("CancelPendingUsage fail %s", err)
|
||||
}
|
||||
|
||||
policyman.PolicyManager.SyncOnce()
|
||||
// policyman.PolicyManager.SyncOnce()
|
||||
}
|
||||
|
||||
func (policy *SPolicy) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
policy.SEnabledIdentityBaseResource.PostUpdate(ctx, userCred, query, data)
|
||||
policyman.PolicyManager.SyncOnce()
|
||||
// policyman.PolicyManager.SyncOnce()
|
||||
}
|
||||
|
||||
func (policy *SPolicy) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
|
||||
policy.SEnabledIdentityBaseResource.PostDelete(ctx, userCred)
|
||||
policyman.PolicyManager.SyncOnce()
|
||||
// policyman.PolicyManager.SyncOnce()
|
||||
}
|
||||
|
||||
func (policy *SPolicy) IsSharable(reqUsrId mcclient.IIdentityProvider) bool {
|
||||
@@ -238,7 +408,7 @@ func (policy *SPolicy) PerformPublic(ctx context.Context, userCred mcclient.Toke
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SharablePerformPublic")
|
||||
}
|
||||
policyman.PolicyManager.SyncOnce()
|
||||
// policyman.PolicyManager.SyncOnce()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -252,7 +422,7 @@ func (policy *SPolicy) PerformPrivate(ctx context.Context, userCred mcclient.Tok
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SharablePerformPrivate")
|
||||
}
|
||||
policyman.PolicyManager.SyncOnce()
|
||||
// policyman.PolicyManager.SyncOnce()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -270,6 +440,9 @@ func (policy *SPolicy) ValidateDeleteCondition(ctx context.Context) error {
|
||||
// if policy.IsShared() {
|
||||
// return httperrors.NewInvalidStatusError("cannot delete shared policy")
|
||||
// }
|
||||
if policy.IsSystem.IsTrue() {
|
||||
return httperrors.NewForbiddenError("cannot delete system policy")
|
||||
}
|
||||
if policy.Enabled.IsTrue() {
|
||||
return httperrors.NewInvalidStatusError("cannot delete enabled policy")
|
||||
}
|
||||
@@ -294,6 +467,13 @@ func (manager *SPolicyManager) ListItemFilter(
|
||||
if len(query.Type) > 0 {
|
||||
q = q.In("type", query.Type)
|
||||
}
|
||||
if query.IsSystem != nil {
|
||||
if *query.IsSystem {
|
||||
q = q.IsTrue("is_system")
|
||||
} else {
|
||||
q = q.IsFalse("is_system")
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
@@ -379,3 +559,29 @@ func (policy *SPolicy) GetRequiredSharedDomainIds() []string {
|
||||
func (policy *SPolicy) GetSharedDomains() []string {
|
||||
return db.SharableGetSharedProjects(policy, db.SharedTargetDomain)
|
||||
}
|
||||
|
||||
func (policy *SPolicy) getPolicy() (rbacutils.TPolicy, error) {
|
||||
pc, err := rbacutils.DecodePolicyData(policy.Blob)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Decode")
|
||||
}
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
func (policy *SPolicy) GetChangeOwnerCandidateDomainIds() []string {
|
||||
return db.ISharableChangeOwnerCandidateDomainIds(policy)
|
||||
}
|
||||
|
||||
func (policy *SPolicy) fetchMatchableRoles() ([]SRole, error) {
|
||||
q := RoleManager.Query()
|
||||
candDomains := policy.GetChangeOwnerCandidateDomainIds()
|
||||
if len(candDomains) > 0 {
|
||||
q = q.In("domain_id", candDomains)
|
||||
}
|
||||
roles := make([]SRole, 0)
|
||||
err := db.FetchModelObjects(RoleManager, q, &roles)
|
||||
if err != nil && errors.Cause(err) != sql.ErrNoRows {
|
||||
return nil, errors.Wrap(err, "FetchRoles")
|
||||
}
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/keystone/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
@@ -480,15 +479,14 @@ func (self *SProject) PostCreate(
|
||||
}
|
||||
}
|
||||
|
||||
func validateJoinProject(userCred mcclient.TokenCredential, project *SProject, roleNames []string) error {
|
||||
opsScope, opsPolicySet := policy.PolicyManager.GetMatchedPolicySet(userCred)
|
||||
rbacCred := rbacutils.NewRbacIdentity(project.DomainId, project.Name, roleNames)
|
||||
assignScope, assignPolicySet := policy.PolicyManager.GetMatchedPolicySet(rbacCred)
|
||||
log.Debugf("opsScope: %s assignScope: %s", opsScope, assignScope)
|
||||
func validateJoinProject(userCred mcclient.TokenCredential, project *SProject, roleIds []string) error {
|
||||
_, opsPolicies, _ := RolePolicyManager.GetMatchPolicyGroup(userCred, false)
|
||||
_, assignPolicies, _ := RolePolicyManager.GetMatchPolicyGroup2(false, roleIds, project.Id, "", false)
|
||||
opsScope := opsPolicies.HighestScope()
|
||||
assignScope := assignPolicies.HighestScope()
|
||||
if assignScope.HigherThan(opsScope) {
|
||||
return errors.Wrap(httperrors.ErrNotSufficientPrivilege, "assigning roles requires higher privilege scope")
|
||||
}
|
||||
if !opsScope.HigherThan(assignScope) && opsPolicySet.ViolatedBy(assignPolicySet) {
|
||||
} else if assignScope == opsScope && opsPolicies[opsScope].ViolatedBy(assignPolicies[assignScope]) {
|
||||
return errors.Wrap(httperrors.ErrNotSufficientPrivilege, "assigning roles violates operator's policy")
|
||||
}
|
||||
return nil
|
||||
@@ -514,7 +512,7 @@ func (project *SProject) PerformJoin(
|
||||
return nil, httperrors.NewInputParameterError("%v", err)
|
||||
}
|
||||
|
||||
roleNames := make([]string, 0)
|
||||
roleIds := make([]string, 0)
|
||||
roles := make([]*SRole, 0)
|
||||
for i := range input.Roles {
|
||||
obj, err := RoleManager.FetchByIdOrName(userCred, input.Roles[i])
|
||||
@@ -527,10 +525,10 @@ func (project *SProject) PerformJoin(
|
||||
}
|
||||
role := obj.(*SRole)
|
||||
roles = append(roles, role)
|
||||
roleNames = append(roleNames, role.Name)
|
||||
roleIds = append(roleIds, role.Id)
|
||||
}
|
||||
|
||||
err = validateJoinProject(userCred, project, roleNames)
|
||||
err = validateJoinProject(userCred, project, roleIds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "validateJoinProject")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,451 @@
|
||||
// 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 models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
type SRolePolicyManager struct {
|
||||
db.SResourceBaseManager
|
||||
}
|
||||
|
||||
var RolePolicyManager *SRolePolicyManager
|
||||
|
||||
func init() {
|
||||
RolePolicyManager = &SRolePolicyManager{
|
||||
SResourceBaseManager: db.NewResourceBaseManager(
|
||||
SRolePolicy{},
|
||||
"rolepolicy_tbl",
|
||||
"rolepolicy",
|
||||
"rolepolicies",
|
||||
),
|
||||
}
|
||||
RolePolicyManager.SetVirtualObject(RolePolicyManager)
|
||||
}
|
||||
|
||||
type SRolePolicy struct {
|
||||
db.SResourceBase
|
||||
|
||||
// 角色ID, 主键
|
||||
RoleId string `width:"128" charset:"ascii" primary:"true" list:"domain" create:"domain_optional"`
|
||||
// 项目ID,主键
|
||||
ProjectId string `width:"128" charset:"ascii" primary:"true" list:"domain" create:"domain_optional"`
|
||||
// 权限ID, 主键
|
||||
PolicyId string `width:"128" charset:"ascii" primary:"true" list:"domain" create:"domain_required"`
|
||||
// 是否需要认证
|
||||
Auth tristate.TriState `nullable:"false" default:"true" list:"domain" create:"domain_optional"`
|
||||
// 匹配的IP白名单
|
||||
Ips string `list:"domain" create:"domain_optional" update:"domain"`
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) newRecord(ctx context.Context, roleId, projectId, policyId string, auth tristate.TriState, ips []netutils.IPV4Prefix) error {
|
||||
if len(roleId) == 0 {
|
||||
return errors.Wrap(httperrors.ErrNotEmpty, "roleId")
|
||||
}
|
||||
if len(policyId) == 0 {
|
||||
return errors.Wrap(httperrors.ErrNotEmpty, "policyId")
|
||||
}
|
||||
rpg := SRolePolicy{}
|
||||
rpg.RoleId = roleId
|
||||
rpg.ProjectId = projectId
|
||||
rpg.PolicyId = policyId
|
||||
rpg.Auth = auth
|
||||
ipStrs := make([]string, len(ips))
|
||||
for i, ipprefix := range ips {
|
||||
ipStrs[i] = ipprefix.String()
|
||||
}
|
||||
rpg.Ips = strings.Join(ipStrs, rbacutils.IP_PREFIX_SEP)
|
||||
rpg.SetModelManager(manager, &rpg)
|
||||
err := RolePolicyManager.TableSpec().InsertOrUpdate(ctx, &rpg)
|
||||
if err != nil {
|
||||
log.Errorf("insert role policy fail %s", err)
|
||||
return errors.Wrap(err, "insert role policy")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) deleteRecord(ctx context.Context, roleId, projectId, policyId string) error {
|
||||
rpg := SRolePolicy{}
|
||||
rpg.RoleId = roleId
|
||||
rpg.ProjectId = projectId
|
||||
rpg.PolicyId = policyId
|
||||
rpg.SetModelManager(manager, &rpg)
|
||||
_, err := db.Update(&rpg, func() error {
|
||||
return rpg.MarkDelete()
|
||||
})
|
||||
if err != nil && errors.Cause(err) != sql.ErrNoRows {
|
||||
return errors.Wrap(err, "Update")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rp *SRolePolicy) GetId() string {
|
||||
return fmt.Sprintf("%s:%s:%s", rp.RoleId, rp.ProjectId, rp.PolicyId)
|
||||
}
|
||||
|
||||
func (rp *SRolePolicy) GetName() string {
|
||||
return getRolePolicyName(rp.GetRole(), rp.GetProject(), rp.GetPolicy())
|
||||
}
|
||||
|
||||
func getRolePolicyName(role *SRole, project *SProject, policy *SPolicy) string {
|
||||
names := make([]string, 0)
|
||||
if role != nil {
|
||||
names = append(names, role.GetName())
|
||||
}
|
||||
if project != nil {
|
||||
names = append(names, project.GetName())
|
||||
}
|
||||
if policy != nil {
|
||||
names = append(names, policy.GetName())
|
||||
}
|
||||
return strings.Join(names, "/")
|
||||
}
|
||||
|
||||
func (rp *SRolePolicy) GetRole() *SRole {
|
||||
role, err := RoleManager.FetchById(rp.RoleId)
|
||||
if err != nil {
|
||||
log.Errorf("RoleManaget.FetchById %s fail %s", rp.RoleId, err)
|
||||
return nil
|
||||
}
|
||||
return role.(*SRole)
|
||||
}
|
||||
|
||||
func (rp *SRolePolicy) GetProject() *SProject {
|
||||
if len(rp.ProjectId) == 0 {
|
||||
return nil
|
||||
}
|
||||
project, err := ProjectManager.FetchById(rp.ProjectId)
|
||||
if err != nil {
|
||||
log.Errorf("ProjectManager.FetchById %s fail %s", rp.ProjectId, err)
|
||||
return nil
|
||||
}
|
||||
return project.(*SProject)
|
||||
}
|
||||
|
||||
func (rp *SRolePolicy) GetPolicy() *SPolicy {
|
||||
policy, err := PolicyManager.FetchById(rp.PolicyId)
|
||||
if err != nil {
|
||||
log.Errorf("PolicyManaget.FetchById %s fail %s", rp.PolicyId, err)
|
||||
return nil
|
||||
}
|
||||
return policy.(*SPolicy)
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) NamespaceScope() rbacutils.TRbacScope {
|
||||
return PolicyManager.NamespaceScope()
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery {
|
||||
return PolicyManager.FilterByOwner(q, owner, scope)
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) ListItemFilter(
|
||||
ctx context.Context,
|
||||
q *sqlchemy.SQuery,
|
||||
userCred mcclient.TokenCredential,
|
||||
query api.RolePolicyListInput,
|
||||
) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
q, err = manager.SResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ResourceBaseListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SResourceBaseManager.ListItemFilter")
|
||||
}
|
||||
if len(query.RoleIds) > 0 {
|
||||
for i := range query.RoleIds {
|
||||
role, err := RoleManager.FetchByIdOrName(userCred, query.RoleIds[i])
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "%s %s", RoleManager.Keyword(), query.RoleIds[i])
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "RoleManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
query.RoleIds[i] = role.GetId()
|
||||
}
|
||||
q = q.Filter(sqlchemy.OR(
|
||||
sqlchemy.IsNullOrEmpty(q.Field("role_id")),
|
||||
sqlchemy.In(q.Field("role_id"), query.RoleIds),
|
||||
))
|
||||
}
|
||||
if len(query.ProjectId) > 0 {
|
||||
project, err := ProjectManager.FetchByIdOrName(userCred, query.ProjectId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "%s %s", ProjectManager.Keyword(), query.ProjectId)
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "ProjectManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
q = q.Filter(sqlchemy.OR(
|
||||
sqlchemy.IsNullOrEmpty(q.Field("project_id")),
|
||||
sqlchemy.Equals(q.Field("project_id"), project.GetId()),
|
||||
))
|
||||
}
|
||||
if len(query.PolicyId) > 0 {
|
||||
policy, err := PolicyManager.FetchByIdOrName(userCred, query.PolicyId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "%s %s", PolicyManager.Keyword(), query.PolicyId)
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "PolicyManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
q = q.Filter(sqlchemy.OR(
|
||||
sqlchemy.IsNullOrEmpty(q.Field("policy_id")),
|
||||
sqlchemy.Equals(q.Field("policy_id"), policy.GetId()),
|
||||
))
|
||||
}
|
||||
if query.Auth != nil {
|
||||
if *query.Auth {
|
||||
q = q.IsTrue("auth")
|
||||
} else {
|
||||
q = q.IsFalse("auth")
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) OrderByExtraFields(
|
||||
ctx context.Context,
|
||||
q *sqlchemy.SQuery,
|
||||
userCred mcclient.TokenCredential,
|
||||
query api.RolePolicyListInput,
|
||||
) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
|
||||
q, err = manager.SResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ResourceBaseListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SResourceBaseManager.OrderByExtraFields")
|
||||
}
|
||||
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
|
||||
q, err = manager.SResourceBaseManager.QueryDistinctExtraField(q, field)
|
||||
if err == nil {
|
||||
return q, nil
|
||||
}
|
||||
|
||||
return q, httperrors.ErrNotFound
|
||||
}
|
||||
|
||||
func (policy *SRolePolicy) GetExtraDetails(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
isList bool,
|
||||
) (api.RolePolicyDetails, error) {
|
||||
return api.RolePolicyDetails{}, nil
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) FetchCustomizeColumns(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
objs []interface{},
|
||||
fields stringutils2.SSortedStrings,
|
||||
isList bool,
|
||||
) []api.RolePolicyDetails {
|
||||
rows := make([]api.RolePolicyDetails, len(objs))
|
||||
resRows := manager.SResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
roleIds := make([]string, 0)
|
||||
projectIds := make([]string, 0)
|
||||
policyIds := make([]string, 0)
|
||||
for i := range rows {
|
||||
rows[i] = api.RolePolicyDetails{
|
||||
ResourceBaseDetails: resRows[i],
|
||||
}
|
||||
rp := objs[i].(*SRolePolicy)
|
||||
roleIds = append(roleIds, rp.RoleId)
|
||||
projectIds = append(projectIds, rp.ProjectId)
|
||||
policyIds = append(policyIds, rp.PolicyId)
|
||||
}
|
||||
|
||||
roleMap := make(map[string]SRole)
|
||||
err := db.FetchModelObjectsByIds(RoleManager, "id", roleIds, &roleMap)
|
||||
if err != nil {
|
||||
log.Errorf("db.FetchModelObjectsByIds RoleManager fail %s", err)
|
||||
return rows
|
||||
}
|
||||
projectMap := make(map[string]SProject)
|
||||
err = db.FetchModelObjectsByIds(ProjectManager, "id", projectIds, &projectMap)
|
||||
if err != nil {
|
||||
log.Errorf("db.FetchModelObjectsByIds ProjectManager fail %s", err)
|
||||
return rows
|
||||
}
|
||||
policyMap := make(map[string]SPolicy)
|
||||
err = db.FetchModelObjectsByIds(PolicyManager, "id", policyIds, &policyMap)
|
||||
if err != nil {
|
||||
log.Errorf("db.FetchModelObjectsByIds PolicyManager fail %s", err)
|
||||
return rows
|
||||
}
|
||||
|
||||
for i := range rows {
|
||||
rp := objs[i].(*SRolePolicy)
|
||||
var role *SRole
|
||||
if obj, ok := roleMap[rp.RoleId]; ok {
|
||||
role = &obj
|
||||
}
|
||||
var project *SProject
|
||||
if obj, ok := projectMap[rp.ProjectId]; ok {
|
||||
project = &obj
|
||||
}
|
||||
var policy *SPolicy
|
||||
if obj, ok := policyMap[rp.PolicyId]; ok {
|
||||
policy = &obj
|
||||
}
|
||||
rows[i].Id = rp.GetId()
|
||||
rows[i].Name = getRolePolicyName(role, project, policy)
|
||||
if role != nil {
|
||||
rows[i].Role = role.GetName()
|
||||
}
|
||||
if project != nil {
|
||||
rows[i].Project = project.GetName()
|
||||
}
|
||||
if policy != nil {
|
||||
rows[i].Policy = policy.GetName()
|
||||
rows[i].Scope = policy.Scope
|
||||
rows[i].Description = policy.Description
|
||||
}
|
||||
}
|
||||
|
||||
return rows
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) getMatchPolicyIds(userCred rbacutils.IRbacIdentity) ([]string, error) {
|
||||
isGuest := true
|
||||
if userCred != nil && !auth.IsGuestToken(userCred) {
|
||||
isGuest = false
|
||||
}
|
||||
return manager.getMatchPolicyIds2(isGuest, userCred.GetRoleIds(), userCred.GetProjectId(), userCred.GetLoginIp())
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) getMatchPolicyIds2(isGuest bool, roleIds []string, pid string, loginIp string) ([]string, error) {
|
||||
q := manager.Query()
|
||||
if !isGuest {
|
||||
if len(roleIds) > 0 {
|
||||
q = q.Filter(sqlchemy.OR(
|
||||
sqlchemy.IsNullOrEmpty(q.Field("role_id")),
|
||||
sqlchemy.In(q.Field("role_id"), roleIds),
|
||||
))
|
||||
}
|
||||
if len(pid) > 0 {
|
||||
q = q.Filter(sqlchemy.OR(
|
||||
sqlchemy.IsNullOrEmpty(q.Field("project_id")),
|
||||
sqlchemy.Equals(q.Field("project_id"), pid),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
q = q.IsFalse("auth")
|
||||
}
|
||||
rps := make([]SRolePolicy, 0)
|
||||
err := db.FetchModelObjects(manager, q, &rps)
|
||||
if err != nil && errors.Cause(err) != sql.ErrNoRows {
|
||||
return nil, errors.Wrap(err, "FetchPolicies")
|
||||
}
|
||||
policyIds := stringutils2.NewSortedStrings(nil)
|
||||
// filter by login IP
|
||||
for _, rp := range rps {
|
||||
if len(loginIp) > 0 && !rp.MatchIP(loginIp) {
|
||||
continue
|
||||
}
|
||||
policyIds = stringutils2.Append(policyIds, rp.PolicyId)
|
||||
}
|
||||
return policyIds, nil
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) GetMatchPolicyGroup(userCred rbacutils.IRbacIdentity, nameOnly bool) (map[rbacutils.TRbacScope][]string, rbacutils.TPolicyGroup, error) {
|
||||
policyIds, err := manager.getMatchPolicyIds(userCred)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "getMatchPolicyIds")
|
||||
}
|
||||
return manager.GetPolicyGroupByIds(policyIds, nameOnly)
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) GetMatchPolicyGroup2(isGuest bool, roleIds []string, pid string, loginIp string, nameOnly bool) (map[rbacutils.TRbacScope][]string, rbacutils.TPolicyGroup, error) {
|
||||
policyIds, err := manager.getMatchPolicyIds2(isGuest, roleIds, pid, loginIp)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "getMatchPolicyIds")
|
||||
}
|
||||
return manager.GetPolicyGroupByIds(policyIds, nameOnly)
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) GetPolicyGroupByIds(policyIds []string, nameOnly bool) (map[rbacutils.TRbacScope][]string, rbacutils.TPolicyGroup, error) {
|
||||
names := make(map[rbacutils.TRbacScope][]string)
|
||||
var group rbacutils.TPolicyGroup
|
||||
if !nameOnly {
|
||||
group = rbacutils.TPolicyGroup{}
|
||||
}
|
||||
for _, id := range policyIds {
|
||||
policyObj, err := PolicyManager.FetchById(id)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "FetchPolicy %s", id)
|
||||
}
|
||||
policy := policyObj.(*SPolicy)
|
||||
if scopeName, ok := names[policy.Scope]; !ok {
|
||||
names[policy.Scope] = []string{policy.Name}
|
||||
} else {
|
||||
names[policy.Scope] = append(scopeName, policy.Name)
|
||||
}
|
||||
if !nameOnly {
|
||||
data, err := policy.getPolicy()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "getPolicy")
|
||||
}
|
||||
if set, ok := group[policy.Scope]; !ok {
|
||||
group[policy.Scope] = rbacutils.TPolicySet{data}
|
||||
} else {
|
||||
group[policy.Scope] = append(set, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
return names, group, nil
|
||||
}
|
||||
|
||||
func (rp *SRolePolicy) MatchIP(ipstr string) bool {
|
||||
return rbacutils.MatchIPStrings(rp.Ips, ipstr)
|
||||
}
|
||||
|
||||
func (manager *SRolePolicyManager) fetchByRoleId(roleId string) ([]SRolePolicy, error) {
|
||||
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 {
|
||||
return nil, errors.Wrap(err, "FetchModelObjects")
|
||||
}
|
||||
return rps, nil
|
||||
}
|
||||
@@ -23,13 +23,14 @@ import (
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/gotypes"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
|
||||
"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"
|
||||
@@ -240,7 +241,13 @@ func (manager *SRoleManager) FetchCustomizeColumns(
|
||||
rows[i].UserCount, _ = role.GetUserCount()
|
||||
rows[i].GroupCount, _ = role.GetGroupCount()
|
||||
rows[i].ProjectCount, _ = role.GetProjectCount()
|
||||
rows[i].MatchPolicies = policy.PolicyManager.RoleMatchPolicies(role.Name)
|
||||
names, _, _ := RolePolicyManager.GetMatchPolicyGroup2(false, []string{role.Id}, "", "", true)
|
||||
rows[i].Policies = names
|
||||
mp := make([]string, 0)
|
||||
for _, v := range names {
|
||||
mp = append(mp, v...)
|
||||
}
|
||||
rows[i].MatchPolicies = mp
|
||||
}
|
||||
|
||||
return rows
|
||||
@@ -347,7 +354,7 @@ func (role *SRole) UpdateInContext(ctx context.Context, userCred mcclient.TokenC
|
||||
if project.DomainId != role.DomainId && !role.GetIsPublic() {
|
||||
return nil, httperrors.NewInputParameterError("inconsistent domain for project and roles")
|
||||
}
|
||||
err := validateJoinProject(userCred, project, []string{role.Name})
|
||||
err := validateJoinProject(userCred, project, []string{role.Id})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "validateJoinProject")
|
||||
}
|
||||
@@ -436,7 +443,6 @@ func (role *SRole) PerformPublic(ctx context.Context, userCred mcclient.TokenCre
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SharablePerformPublic")
|
||||
}
|
||||
policy.PolicyManager.SyncOnce()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -449,7 +455,6 @@ func (role *SRole) PerformPrivate(ctx context.Context, userCred mcclient.TokenCr
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SharablePerformPrivate")
|
||||
}
|
||||
policy.PolicyManager.SyncOnce()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -541,3 +546,171 @@ func (role *SRole) GetRequiredSharedDomainIds() []string {
|
||||
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 (role *SRole) PerformSetPolicies(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RolePerformSetPoliciesInput) (jsonutils.JSONObject, error) {
|
||||
normalInputIds := stringutils2.NewSortedStrings(nil)
|
||||
normalInputs := make(map[string]sRolePerformAddPolicyInput, len(input.Policies))
|
||||
for i := range input.Policies {
|
||||
normalInput, err := role.normalizeRoleAddPolicyInput(userCred, input.Policies[i])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "normalizeRoleAddPolicyInput at %d", i)
|
||||
}
|
||||
idstr := normalInput.getId()
|
||||
if _, ok := normalInputs[idstr]; !ok {
|
||||
normalInputs[idstr] = normalInput
|
||||
normalInputIds = stringutils2.Append(normalInputIds, idstr)
|
||||
} else {
|
||||
log.Warningf("duplicate input key id %s", idstr)
|
||||
}
|
||||
}
|
||||
|
||||
existRpList, err := RolePolicyManager.fetchByRoleId(role.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "RolePolicyManager.fetchByRoleId")
|
||||
}
|
||||
|
||||
existRpIds := stringutils2.NewSortedStrings(nil)
|
||||
existRpMap := make(map[string]*SRolePolicy)
|
||||
for i := range existRpList {
|
||||
idstr := existRpList[i].GetId()
|
||||
if _, ok := existRpMap[idstr]; !ok {
|
||||
existRpMap[idstr] = &existRpList[i]
|
||||
existRpIds = stringutils2.Append(existRpIds, idstr)
|
||||
}
|
||||
}
|
||||
|
||||
addedIds, updatedIds, deletedIds := stringutils2.Split(normalInputIds, existRpIds)
|
||||
|
||||
for _, idstr := range deletedIds {
|
||||
toDel := existRpMap[idstr]
|
||||
err := RolePolicyManager.deleteRecord(ctx, toDel.RoleId, toDel.ProjectId, toDel.PolicyId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "RolePolicyManager.deleteRecord")
|
||||
}
|
||||
}
|
||||
|
||||
for _, idstr := range updatedIds {
|
||||
toUpdate := normalInputs[idstr]
|
||||
err := RolePolicyManager.newRecord(ctx, toUpdate.roleId, toUpdate.projectId, toUpdate.policyId, tristate.True, toUpdate.prefixes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "RolePolicyManager.updateRecord")
|
||||
}
|
||||
}
|
||||
|
||||
for _, idstr := range addedIds {
|
||||
toAdd := normalInputs[idstr]
|
||||
err := RolePolicyManager.newRecord(ctx, toAdd.roleId, toAdd.projectId, toAdd.policyId, tristate.True, toAdd.prefixes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "RolePolicyManager.newRecord")
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
projectId string
|
||||
policyId string
|
||||
}
|
||||
|
||||
func (s sRolePerformAddPolicyInput) getId() string {
|
||||
return fmt.Sprintf("%s:%s:%s", s.roleId, s.projectId, s.policyId)
|
||||
}
|
||||
|
||||
func (role *SRole) normalizeRoleAddPolicyInput(userCred mcclient.TokenCredential, input api.RolePerformAddPolicyInput) (sRolePerformAddPolicyInput, error) {
|
||||
output := sRolePerformAddPolicyInput{}
|
||||
prefList := make([]netutils.IPV4Prefix, 0)
|
||||
for _, ipStr := range input.Ips {
|
||||
pref, err := netutils.NewIPV4Prefix(ipStr)
|
||||
if err != nil {
|
||||
return output, errors.Wrapf(httperrors.ErrInputParameter, "invalid prefix %s", ipStr)
|
||||
}
|
||||
prefList = append(prefList, pref)
|
||||
}
|
||||
if len(input.ProjectId) > 0 {
|
||||
proj, err := ProjectManager.FetchByIdOrName(userCred, input.ProjectId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return output, errors.Wrapf(httperrors.ErrNotFound, "%s %s", ProjectManager.Keyword(), input.ProjectId)
|
||||
} else {
|
||||
return output, errors.Wrap(err, "ProjectManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
output.projectId = proj.GetId()
|
||||
}
|
||||
if len(input.PolicyId) == 0 {
|
||||
return output, errors.Wrap(httperrors.ErrInputParameter, "missing policy_id")
|
||||
}
|
||||
policy, err := PolicyManager.FetchByIdOrName(userCred, input.PolicyId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return output, errors.Wrapf(httperrors.ErrNotFound, "%s %s", PolicyManager.Keyword(), input.PolicyId)
|
||||
} else {
|
||||
return output, errors.Wrap(err, "PolicyManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
output.roleId = role.Id
|
||||
output.prefixes = prefList
|
||||
output.policyId = policy.GetId()
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (role *SRole) PerformAddPolicy(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RolePerformAddPolicyInput) (jsonutils.JSONObject, error) {
|
||||
normalInput, err := role.normalizeRoleAddPolicyInput(userCred, input)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "normalizeRoleAddPolicyInput")
|
||||
}
|
||||
err = RolePolicyManager.newRecord(ctx, normalInput.roleId, normalInput.projectId, normalInput.policyId, tristate.True, normalInput.prefixes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "newRecord")
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return nil, errors.Wrapf(httperrors.ErrNotFound, "%s %s", ProjectManager.Keyword(), input.ProjectId)
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "ProjectManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
input.ProjectId = proj.GetId()
|
||||
}
|
||||
if len(input.PolicyId) == 0 {
|
||||
return nil, errors.Wrap(httperrors.ErrInputParameter, "missing policy_id")
|
||||
}
|
||||
policy, err := PolicyManager.FetchByIdOrName(userCred, input.PolicyId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return nil, errors.Wrapf(httperrors.ErrNotFound, "%s %s", PolicyManager.Keyword(), input.PolicyId)
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "PolicyManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
err = RolePolicyManager.deleteRecord(ctx, role.Id, input.ProjectId, policy.GetId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "deleteRecord")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (role *SRole) GetChangeOwnerCandidateDomainIds() []string {
|
||||
return db.ISharableChangeOwnerCandidateDomainIds(role)
|
||||
}
|
||||
|
||||
@@ -201,9 +201,9 @@ func (service *SService) PerformConfig(ctx context.Context, userCred mcclient.To
|
||||
action := input.Action
|
||||
opts := input.Config
|
||||
if service.isCommonService() {
|
||||
err = saveConfigs(userCred, action, service, opts, api.CommonWhitelistOptionMap, nil, nil)
|
||||
err = saveConfigs(userCred, action, service, opts, api.CommonWhitelistOptionMap, nil, nil, false)
|
||||
} else {
|
||||
err = saveConfigs(userCred, action, service, opts, nil, api.ServiceBlacklistOptionMap, nil)
|
||||
err = saveConfigs(userCred, action, service, opts, nil, api.ServiceBlacklistOptionMap, nil, false)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInternalServerError("saveConfig fail %s", err)
|
||||
|
||||
@@ -86,7 +86,7 @@ type SUser struct {
|
||||
LastLoginIp string `nullable:"true" list:"domain"`
|
||||
LastLoginSource string `nullable:"true" list:"domain"`
|
||||
|
||||
IsSystemAccount tristate.TriState `nullable:"false" default:"false" list:"domain" update:"domain" create:"domain_optional"`
|
||||
IsSystemAccount tristate.TriState `nullable:"false" default:"false" list:"domain" update:"admin" create:"admin_optional"`
|
||||
|
||||
// deprecated
|
||||
DefaultProjectId string `width:"64" charset:"ascii" nullable:"true"`
|
||||
@@ -957,7 +957,7 @@ func joinProjects(ident db.IModel, isUser bool, ctx context.Context, userCred mc
|
||||
|
||||
projects := make([]*SProject, 0)
|
||||
roles := make([]*SRole, 0)
|
||||
roleNames := make([]string, 0)
|
||||
roleIds := make([]string, 0)
|
||||
|
||||
for i := range input.Roles {
|
||||
obj, err := RoleManager.FetchByIdOrName(userCred, input.Roles[i])
|
||||
@@ -970,7 +970,7 @@ func joinProjects(ident db.IModel, isUser bool, ctx context.Context, userCred mc
|
||||
}
|
||||
role := obj.(*SRole)
|
||||
roles = append(roles, role)
|
||||
roleNames = append(roleNames, role.Name)
|
||||
roleIds = append(roleIds, role.Id)
|
||||
}
|
||||
|
||||
for i := range input.Projects {
|
||||
@@ -983,7 +983,7 @@ func joinProjects(ident db.IModel, isUser bool, ctx context.Context, userCred mc
|
||||
}
|
||||
}
|
||||
project := obj.(*SProject)
|
||||
err = validateJoinProject(userCred, project, roleNames)
|
||||
err = validateJoinProject(userCred, project, roleIds)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "validateJoinProject %s(%s)", project.Id, project.Name)
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ func InitHandlers(app *appsrv.Application) {
|
||||
models.CredentialManager,
|
||||
models.IdentityProviderManager,
|
||||
models.ServiceCertificateManager,
|
||||
models.RolePolicyManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
handler := db.NewModelHandler(manager)
|
||||
|
||||
@@ -17,58 +17,21 @@ package service
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/keystone/models"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
func localPolicyFetcher(ctx context.Context) (map[rbacutils.TRbacScope][]rbacutils.SPolicyInfo, error) {
|
||||
policyList, err := models.PolicyManager.FetchEnabledPolicies()
|
||||
func localPolicyFetcher(ctx context.Context, token mcclient.TokenCredential) (*mcclient.SFetchMatchPoliciesOutput, error) {
|
||||
names, groups, err := models.RolePolicyManager.GetMatchPolicyGroup(token, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "models.PolicyManager.FetchEnabledPolicies")
|
||||
return nil, errors.Wrap(err, "GetMatchPolicyGroup")
|
||||
}
|
||||
|
||||
policies := make(map[rbacutils.TRbacScope][]rbacutils.SPolicyInfo)
|
||||
output := mcclient.SFetchMatchPoliciesOutput{}
|
||||
output.Names = names
|
||||
output.Policies = groups
|
||||
|
||||
for i := range policyList {
|
||||
typeStr := policyList[i].Name
|
||||
policy := rbacutils.SRbacPolicy{}
|
||||
policyStr, err := policyList[i].Blob.GetString()
|
||||
if err != nil {
|
||||
log.Errorf("fail to get string of blob %s", err)
|
||||
continue
|
||||
}
|
||||
policyJson, err := jsonutils.ParseString(policyStr)
|
||||
if err != nil {
|
||||
log.Errorf("fail to deocde policy blob into JSON %s", err)
|
||||
continue
|
||||
}
|
||||
err = policy.Decode(policyJson)
|
||||
if err != nil {
|
||||
log.Errorf("fail to decode policy %s %s %s", typeStr, policyList[i].Blob, err)
|
||||
continue
|
||||
}
|
||||
|
||||
policy.DomainId = policyList[i].DomainId
|
||||
policy.IsPublic = policyList[i].IsPublic
|
||||
policy.PublicScope = rbacutils.String2ScopeDefault(policyList[i].PublicScope, rbacutils.ScopeSystem)
|
||||
policy.SharedDomainIds = policyList[i].GetSharedDomains()
|
||||
|
||||
if _, ok := policies[policy.Scope]; !ok {
|
||||
policies[policy.Scope] = make([]rbacutils.SPolicyInfo, 0)
|
||||
}
|
||||
|
||||
sp := rbacutils.SPolicyInfo{
|
||||
Id: policyList[i].Id,
|
||||
Name: policyList[i].Name,
|
||||
Policy: &policy,
|
||||
}
|
||||
|
||||
policies[policy.Scope] = append(policies[policy.Scope], sp)
|
||||
}
|
||||
|
||||
return policies, nil
|
||||
return &output, nil
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ func AddHandler(app *appsrv.Application) {
|
||||
app.AddHandler2("POST", "/v3/auth/tokens", authenticateTokensV3, nil, "auth_tokens_v3", nil)
|
||||
app.AddHandler2("GET", "/v2.0/tokens/<token>", authenticateToken(verifyTokensV2), nil, "verify_tokens_v2", nil)
|
||||
app.AddHandler2("GET", "/v3/auth/tokens", authenticateToken(verifyTokensV3), nil, "verify_tokens_v3", nil)
|
||||
app.AddHandler2("GET", "/v3/auth/policies", authenticateToken(fetchTokenPolicies), nil, "fetch_token_policies", nil)
|
||||
}
|
||||
|
||||
func FetchAuthContext(authCtx mcclient.SAuthContext, r *http.Request) mcclient.SAuthContext {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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 tokens
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/keystone/models"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
func fetchTokenPolicies(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
token := policy.FetchUserCredential(ctx)
|
||||
names, group, err := models.RolePolicyManager.GetMatchPolicyGroup(token, false)
|
||||
if err != nil {
|
||||
httperrors.GeneralServerError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
output := mcclient.SFetchMatchPoliciesOutput{}
|
||||
output.Names = names
|
||||
output.Policies = group
|
||||
appsrv.SendJSON(w, output.Encode())
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
||||
"yunion.io/x/onecloud/pkg/keystone/keys"
|
||||
"yunion.io/x/onecloud/pkg/keystone/models"
|
||||
"yunion.io/x/onecloud/pkg/keystone/options"
|
||||
@@ -219,10 +218,13 @@ func (t *SAuthToken) GetSimpleUserCred(token string) (mcclient.TokenCredential,
|
||||
roles, err = models.AssignmentManager.FetchUserProjectRoles(t.UserId, t.DomainId)
|
||||
}
|
||||
roleStrs := make([]string, len(roles))
|
||||
roleIdStrs := make([]string, len(roles))
|
||||
for i := range roles {
|
||||
roleStrs[i] = roles[i].Name
|
||||
roleIdStrs[i] = roles[i].Id
|
||||
}
|
||||
ret.Roles = strings.Join(roleStrs, ",")
|
||||
ret.RoleIds = strings.Join(roleIdStrs, ",")
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
@@ -334,9 +336,10 @@ func (t *SAuthToken) getTokenV3(
|
||||
token.Token.Roles[i].Name = roles[i].Name
|
||||
}
|
||||
|
||||
token.Token.Policies.Project = policy.PolicyManager.MatchedPolicyNames(rbacutils.ScopeProject, &token)
|
||||
token.Token.Policies.Domain = policy.PolicyManager.MatchedPolicyNames(rbacutils.ScopeDomain, &token)
|
||||
token.Token.Policies.System = policy.PolicyManager.MatchedPolicyNames(rbacutils.ScopeSystem, &token)
|
||||
policyNames, _, _ := models.RolePolicyManager.GetMatchPolicyGroup(&token, true)
|
||||
token.Token.Policies.Project, _ = policyNames[rbacutils.ScopeProject]
|
||||
token.Token.Policies.Domain, _ = policyNames[rbacutils.ScopeDomain]
|
||||
token.Token.Policies.System, _ = policyNames[rbacutils.ScopeSystem]
|
||||
|
||||
endpoints, err := models.EndpointManager.FetchAll()
|
||||
if err != nil {
|
||||
@@ -400,6 +403,7 @@ func (t *SAuthToken) getTokenV2(
|
||||
token.Metadata.Roles = make([]string, len(roles))
|
||||
for i := range roles {
|
||||
token.User.Roles[i].Name = roles[i].Name
|
||||
token.User.Roles[i].Id = roles[i].Id
|
||||
token.Metadata.Roles[i] = roles[i].Name
|
||||
}
|
||||
endpoints, err := models.EndpointManager.FetchAll()
|
||||
|
||||
@@ -25,11 +25,15 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
)
|
||||
|
||||
var (
|
||||
GuestToken = mcclient.SSimpleToken{
|
||||
User: "guest",
|
||||
GUEST_USER = "guest"
|
||||
GUEST_TOKEN = "guest_token"
|
||||
GuestToken = mcclient.SSimpleToken{
|
||||
User: GUEST_USER,
|
||||
Token: GUEST_TOKEN,
|
||||
}
|
||||
|
||||
DefaultTokenVerifier = Verify
|
||||
@@ -91,3 +95,7 @@ func FetchUserCredential(ctx context.Context, filter func(mcclient.TokenCredenti
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsGuestToken(userCred rbacutils.IRbacIdentity) bool {
|
||||
return userCred.GetTokenString() == GUEST_TOKEN
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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 auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
func (a *authManager) fetchMatchPolicies(ctx context.Context, token mcclient.TokenCredential) (*mcclient.SFetchMatchPoliciesOutput, error) {
|
||||
return a.client.FetchMatchPolicies(ctx, token)
|
||||
}
|
||||
|
||||
func FetchMatchPolicies(ctx context.Context, token mcclient.TokenCredential) (*mcclient.SFetchMatchPoliciesOutput, error) {
|
||||
return manager.fetchMatchPolicies(ctx, token)
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/seclib2"
|
||||
)
|
||||
|
||||
@@ -412,3 +413,48 @@ func (this *Client) NewSession(ctx context.Context, region, zone, endpointType s
|
||||
customizeServiceUrl: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
type SFetchMatchPoliciesOutput struct {
|
||||
Names map[rbacutils.TRbacScope][]string `json:"names"`
|
||||
Policies rbacutils.TPolicyGroup `json:"policies"`
|
||||
}
|
||||
|
||||
func (o *SFetchMatchPoliciesOutput) Decode(object jsonutils.JSONObject) error {
|
||||
err := object.Unmarshal(&o.Names, "names")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unmarshal names")
|
||||
}
|
||||
pData, err := object.Get("policies")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Get policies")
|
||||
}
|
||||
o.Policies, err = rbacutils.DecodePolicyGroup(pData)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "DecodePolicyGroup")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o SFetchMatchPoliciesOutput) Encode() jsonutils.JSONObject {
|
||||
output := jsonutils.NewDict()
|
||||
output.Set("names", jsonutils.Marshal(o.Names))
|
||||
output.Set("policies", o.Policies.Encode())
|
||||
return output
|
||||
}
|
||||
|
||||
func (client *Client) FetchMatchPolicies(ctx context.Context, token TokenCredential) (*SFetchMatchPoliciesOutput, error) {
|
||||
header := http.Header{}
|
||||
if token.GetTokenString() != "" {
|
||||
header.Add(api.AUTH_TOKEN_HEADER, token.GetTokenString())
|
||||
}
|
||||
_, rbody, err := client.jsonRequest(ctx, client.authUrl, "", "GET", "/auth/policies", header, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "jsonRequest")
|
||||
}
|
||||
output := &SFetchMatchPoliciesOutput{}
|
||||
err = output.Decode(rbody)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SFetchMatchPoliciesOutput.Decode")
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package modules
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
@@ -30,22 +31,18 @@ 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", "project_domain", "can_update", "can_delete", "is_public", "description", "delete_fail_reason", "update_fail_reason", "public_scope", "shared_domains", "created_at", "updated_at")
|
||||
ret := ss.CopyExcludes("blob", "type")
|
||||
blobJson, _ := ss.Get("blob")
|
||||
if blobJson != nil {
|
||||
policy := rbacutils.SRbacPolicy{}
|
||||
blobStr, _ := blobJson.GetString()
|
||||
if len(blobStr) > 0 {
|
||||
blobJson, _ = jsonutils.ParseString(blobStr)
|
||||
}
|
||||
err := policy.Decode(blobJson)
|
||||
policy, err := rbacutils.DecodePolicyData(blobJson)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blobJson, err = policy.Encode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "rbacutils.DecodePolicyData")
|
||||
}
|
||||
blobJson = policy.EncodeData()
|
||||
var format string
|
||||
if query != nil {
|
||||
format, _ = query.GetString("format")
|
||||
@@ -82,7 +79,7 @@ func policyWriteFilter(session *mcclient.ClientSession, s jsonutils.JSONObject,
|
||||
ret.Add(blobJson, "blob")
|
||||
}
|
||||
for _, k := range []string{
|
||||
"type", "enabled", "domain", "domain_id", "project_domain", "description", "is_public", "public_scope", "shared_domains",
|
||||
"name", "type", "enabled", "domain", "domain_id", "project_domain", "description", "is_public", "public_scope", "shared_domains", "scope", "is_system",
|
||||
} {
|
||||
if s.Contains(k) {
|
||||
val, err := s.Get(k)
|
||||
@@ -97,14 +94,15 @@ func policyWriteFilter(session *mcclient.ClientSession, s jsonutils.JSONObject,
|
||||
|
||||
func init() {
|
||||
Policies = SPolicyManager{NewIdentityV3Manager(
|
||||
"policy", "policies",
|
||||
[]string{"id", "type", "policy", "enabled",
|
||||
"domain_id", "domain", "project_domain",
|
||||
"is_public", "description",
|
||||
"policy",
|
||||
"policies",
|
||||
[]string{"id", "name", "policy", "scope", "enabled",
|
||||
"domain_id", "domain", "project_domain", "public_scope",
|
||||
"is_public", "description", "is_system",
|
||||
},
|
||||
[]string{})}
|
||||
|
||||
Policies.SetReadFilter(policyReadFilter).SetWriteFilter(policyWriteFilter).SetNameField("type")
|
||||
Policies.SetReadFilter(policyReadFilter).SetWriteFilter(policyWriteFilter) // .SetNameField("type")
|
||||
|
||||
register(&Policies)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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 modules
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
)
|
||||
|
||||
type SRolePolicyManager struct {
|
||||
modulebase.ResourceManager
|
||||
}
|
||||
|
||||
var RolePolicies SRolePolicyManager
|
||||
|
||||
func (manager *SRolePolicyManager) FetchMatchedPolicies(s *mcclient.ClientSession, roleIds []string, projectId string, loginIp string) (map[string][]string, error) {
|
||||
input := api.RolePolicyListInput{}
|
||||
input.RoleIds = roleIds
|
||||
input.ProjectId = projectId
|
||||
limit := 2048
|
||||
input.Limit = &limit
|
||||
details := true
|
||||
input.Details = &details
|
||||
results, err := manager.List(s, jsonutils.Marshal(input))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "List RolePolicyManager")
|
||||
}
|
||||
ret := make(map[string][]string)
|
||||
for i := range results.Data {
|
||||
policy, _ := results.Data[i].GetString("policy")
|
||||
scope, _ := results.Data[i].GetString("scope")
|
||||
if policies, ok := ret[scope]; !ok {
|
||||
ret[scope] = []string{policy}
|
||||
} else {
|
||||
ret[scope] = append(policies, policy)
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RolePolicies = SRolePolicyManager{NewIdentityV3Manager(
|
||||
"rolepolicy",
|
||||
"rolepolicies",
|
||||
[]string{"role", "role_id", "project", "project_id", "policy", "policy_id", "ips", "scope"},
|
||||
[]string{},
|
||||
)}
|
||||
|
||||
register(&RolePolicies)
|
||||
}
|
||||
@@ -74,6 +74,7 @@ type TokenCredential interface {
|
||||
|
||||
GetTokenString() string
|
||||
GetRoles() []string
|
||||
GetRoleIds() []string
|
||||
GetExpires() time.Time
|
||||
IsValid() bool
|
||||
ValidDuration() time.Duration
|
||||
|
||||
@@ -51,6 +51,8 @@ type KeystoneServiceV2 struct {
|
||||
type KeystoneRoleV2 struct {
|
||||
// 角色名称
|
||||
Name string `json:"name"`
|
||||
// 角色ID
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type KeystoneUserV2 struct {
|
||||
@@ -168,6 +170,14 @@ func (token *TokenCredentialV2) GetRoles() []string {
|
||||
return roles
|
||||
}
|
||||
|
||||
func (token *TokenCredentialV2) GetRoleIds() []string {
|
||||
roles := make([]string, 0)
|
||||
for i := 0; i < len(token.User.Roles); i++ {
|
||||
roles = append(roles, token.User.Roles[i].Id)
|
||||
}
|
||||
return roles
|
||||
}
|
||||
|
||||
func (this *TokenCredentialV2) GetExpires() time.Time {
|
||||
return this.Token.Expires
|
||||
}
|
||||
|
||||
@@ -202,6 +202,14 @@ func (token *TokenCredentialV3) GetRoles() []string {
|
||||
return roles
|
||||
}
|
||||
|
||||
func (token *TokenCredentialV3) GetRoleIds() []string {
|
||||
roles := make([]string, 0)
|
||||
for i := 0; i < len(token.Token.Roles); i++ {
|
||||
roles = append(roles, token.Token.Roles[i].Id)
|
||||
}
|
||||
return roles
|
||||
}
|
||||
|
||||
func (this *TokenCredentialV3) GetExpires() time.Time {
|
||||
return this.Token.ExpiresAt
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ type SSimpleToken struct {
|
||||
ProjectDomainId string
|
||||
|
||||
Roles string
|
||||
RoleIds string
|
||||
Expires time.Time
|
||||
|
||||
Context SAuthContext
|
||||
@@ -99,6 +100,10 @@ func (self *SSimpleToken) GetRoles() []string {
|
||||
return strings.Split(self.Roles, ",")
|
||||
}
|
||||
|
||||
func (self *SSimpleToken) GetRoleIds() []string {
|
||||
return strings.Split(self.RoleIds, ",")
|
||||
}
|
||||
|
||||
func (self *SSimpleToken) GetExpires() time.Time {
|
||||
return self.Expires
|
||||
}
|
||||
@@ -194,6 +199,7 @@ func SimplifyToken(token TokenCredential) TokenCredential {
|
||||
ProjectDomainId: token.GetProjectDomainId(),
|
||||
|
||||
Roles: strings.Join(token.GetRoles(), ","),
|
||||
RoleIds: strings.Join(token.GetRoleIds(), ","),
|
||||
Expires: token.GetExpires(),
|
||||
Context: SAuthContext{
|
||||
Source: token.GetLoginSource(),
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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 rbacutils
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
const (
|
||||
IP_PREFIX_SEP = ","
|
||||
)
|
||||
|
||||
func getPrefixes(prefstr string) []netutils.IPV4Prefix {
|
||||
if len(prefstr) == 0 {
|
||||
return nil
|
||||
}
|
||||
prefs := strings.Split(prefstr, IP_PREFIX_SEP)
|
||||
ret := make([]netutils.IPV4Prefix, 0)
|
||||
for _, pref := range prefs {
|
||||
p, err := netutils.NewIPV4Prefix(pref)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, p)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func MatchIPStrings(prefstr string, ipstr string) bool {
|
||||
prefs := getPrefixes(prefstr)
|
||||
return matchIP(prefs, ipstr)
|
||||
}
|
||||
|
||||
func matchIP(prefs []netutils.IPV4Prefix, ipstr string) bool {
|
||||
if len(prefs) == 0 {
|
||||
return true
|
||||
}
|
||||
ip, err := netutils.NewIPV4Addr(ipstr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, pref := range prefs {
|
||||
if pref.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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 rbacutils
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMatchIPStrings(t *testing.T) {
|
||||
cases := []struct {
|
||||
prefixes string
|
||||
ip string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
prefixes: "",
|
||||
ip: "127.0.0.1",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
prefixes: "10.0.0.0/8",
|
||||
ip: "10.8.0.1",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
prefixes: "10.0.0.0/8,192.168.0.0/16",
|
||||
ip: "172.16.0.23",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
prefixes: "10.0.0.0/8,192.168.0.0/16",
|
||||
ip: "10.16.0.23",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
prefixes: "10.0.0.0/8,192.168.0.0/16",
|
||||
ip: "192.168.0.23",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := MatchIPStrings(c.prefixes, c.ip)
|
||||
if got != c.want {
|
||||
t.Errorf("prefix %s ip %s got %v want %v", c.prefixes, c.ip, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
// 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 rbacutils
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
type SRbacPolicy struct {
|
||||
// condition, when the policy takes effects
|
||||
// Deprecated
|
||||
Condition string
|
||||
|
||||
DomainId string
|
||||
|
||||
IsPublic bool
|
||||
|
||||
PublicScope TRbacScope
|
||||
|
||||
SharedDomainIds []string
|
||||
|
||||
Projects []string
|
||||
|
||||
Roles []string
|
||||
|
||||
Ips []netutils.IPV4Prefix
|
||||
|
||||
Auth bool // whether needs authentication
|
||||
|
||||
// scope, the scope of the policy, system/domain/project
|
||||
Scope TRbacScope
|
||||
// Deprecated
|
||||
// is_admin=true means scope=system, is_admin=false means scope=project
|
||||
IsAdmin bool
|
||||
|
||||
Rules TPolicy
|
||||
}
|
||||
|
||||
var (
|
||||
tenantEqualsPattern = regexp.MustCompile(`tenant\s*==\s*['"]?(\w+)['"]?`)
|
||||
roleContainsPattern = regexp.MustCompile(`roles.contains\(['"]?(\w+)['"]?\)`)
|
||||
)
|
||||
|
||||
func searchMatchStrings(pattern *regexp.Regexp, condstr string) []string {
|
||||
ret := make([]string, 0)
|
||||
matches := pattern.FindAllStringSubmatch(condstr, -1)
|
||||
for _, match := range matches {
|
||||
ret = append(ret, match[1])
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func searchMatchTenants(condstr string) []string {
|
||||
return searchMatchStrings(tenantEqualsPattern, condstr)
|
||||
}
|
||||
|
||||
func searchMatchRoles(condstr string) []string {
|
||||
return searchMatchStrings(roleContainsPattern, condstr)
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) Decode(policyJson jsonutils.JSONObject) error {
|
||||
policy.Condition, _ = policyJson.GetString("condition")
|
||||
if policyJson.Contains("projects") {
|
||||
projectJson, _ := policyJson.GetArray("projects")
|
||||
policy.Projects = jsonutils.JSONArray2StringArray(projectJson)
|
||||
}
|
||||
if policyJson.Contains("roles") {
|
||||
roleJson, _ := policyJson.GetArray("roles")
|
||||
policy.Roles = jsonutils.JSONArray2StringArray(roleJson)
|
||||
}
|
||||
|
||||
if len(policy.Projects) == 0 && len(policy.Roles) == 0 && len(policy.Condition) > 0 {
|
||||
// XXX hack
|
||||
// for smooth transtion from condition to projects&roles
|
||||
policy.Projects = searchMatchTenants(policy.Condition)
|
||||
policy.Roles = searchMatchRoles(policy.Condition)
|
||||
}
|
||||
// empty condition, no longer use this field
|
||||
policy.Condition = ""
|
||||
|
||||
if policyJson.Contains("ips") {
|
||||
ipsJson, _ := policyJson.GetArray("ips")
|
||||
ipStrs := jsonutils.JSONArray2StringArray(ipsJson)
|
||||
policy.Ips = make([]netutils.IPV4Prefix, 0)
|
||||
for _, ipStr := range ipStrs {
|
||||
if len(ipStr) == 0 || ipStr == "0.0.0.0" {
|
||||
continue
|
||||
}
|
||||
prefix, err := netutils.NewIPV4Prefix(ipStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
policy.Ips = append(policy.Ips, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
policy.Auth = jsonutils.QueryBoolean(policyJson, "auth", true)
|
||||
if len(policy.Ips) > 0 || len(policy.Roles) > 0 || len(policy.Projects) > 0 {
|
||||
policy.Auth = true
|
||||
}
|
||||
|
||||
scopeStr, _ := policyJson.GetString("scope")
|
||||
if len(scopeStr) > 0 {
|
||||
policy.Scope = TRbacScope(scopeStr)
|
||||
} else {
|
||||
policy.IsAdmin = jsonutils.QueryBoolean(policyJson, "is_admin", false)
|
||||
if len(policy.Scope) == 0 {
|
||||
if policy.IsAdmin {
|
||||
policy.Scope = ScopeSystem
|
||||
} else {
|
||||
policy.Scope = ScopeProject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
policyBody, err := policyJson.Get("policy")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Get policy")
|
||||
}
|
||||
policy.Rules, err = DecodePolicy(policyBody)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "DecodePolicy")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) Encode() jsonutils.JSONObject {
|
||||
ret := jsonutils.NewDict()
|
||||
|
||||
if !policy.Auth && len(policy.Projects) == 0 && len(policy.Roles) == 0 && len(policy.Ips) == 0 {
|
||||
ret.Add(jsonutils.JSONFalse, "auth")
|
||||
} else {
|
||||
ret.Add(jsonutils.JSONTrue, "auth")
|
||||
}
|
||||
|
||||
if len(policy.Projects) > 0 {
|
||||
ret.Add(jsonutils.NewStringArray(policy.Projects), "projects")
|
||||
}
|
||||
if len(policy.Roles) > 0 {
|
||||
ret.Add(jsonutils.NewStringArray(policy.Roles), "roles")
|
||||
}
|
||||
if len(policy.Ips) > 0 {
|
||||
ipStrs := make([]string, len(policy.Ips))
|
||||
for i := range policy.Ips {
|
||||
ipStrs[i] = policy.Ips[i].String()
|
||||
}
|
||||
ret.Add(jsonutils.NewStringArray(ipStrs), "ips")
|
||||
}
|
||||
|
||||
ret.Add(jsonutils.NewString(string(policy.Scope)), "scope")
|
||||
|
||||
ret.Add(policy.Rules.Encode(), "policy")
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) IsSystemWidePolicy() bool {
|
||||
return (len(policy.DomainId) == 0 || (policy.IsPublic && policy.PublicScope == ScopeSystem)) && len(policy.Roles) == 0 && len(policy.Projects) == 0
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchDomain(domainId string) bool {
|
||||
if len(policy.DomainId) == 0 || len(domainId) == 0 {
|
||||
return true
|
||||
}
|
||||
if policy.DomainId == domainId {
|
||||
return true
|
||||
}
|
||||
if policy.IsPublic {
|
||||
if policy.PublicScope == ScopeSystem {
|
||||
return true
|
||||
}
|
||||
if contains(policy.SharedDomainIds, domainId) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchProject(projectName string) bool {
|
||||
if len(policy.Projects) == 0 || len(projectName) == 0 {
|
||||
return true
|
||||
}
|
||||
if contains(policy.Projects, projectName) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchRoles(roleNames []string) bool {
|
||||
if len(policy.Roles) == 0 {
|
||||
return true
|
||||
}
|
||||
if intersect(policy.Roles, roleNames) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// check whether policy maches a userCred
|
||||
// return value
|
||||
// bool isMatched
|
||||
// int match weight, the higher the value, the more exact the match
|
||||
// the more exact match wins
|
||||
func (policy *SRbacPolicy) Match(userCred IRbacIdentity2) (bool, int) {
|
||||
if !policy.Auth && len(policy.Roles) == 0 && len(policy.Projects) == 0 && len(policy.Ips) == 0 {
|
||||
return true, 1
|
||||
}
|
||||
if userCred == nil || len(userCred.GetTokenString()) == 0 {
|
||||
return false, 0
|
||||
}
|
||||
weight := 0
|
||||
if policy.MatchDomain(userCred.GetProjectDomainId()) {
|
||||
if len(policy.DomainId) > 0 {
|
||||
if policy.DomainId == userCred.GetProjectDomainId() {
|
||||
weight += 30 // exact domain match
|
||||
} else if len(policy.SharedDomainIds) > 0 {
|
||||
weight += 20 // shared domain match
|
||||
} else {
|
||||
weight += 10 // else, system scope match
|
||||
}
|
||||
}
|
||||
if policy.MatchRoles(userCred.GetRoles()) {
|
||||
if len(policy.Roles) != 0 {
|
||||
weight += 100
|
||||
}
|
||||
if policy.MatchProject(userCred.GetProjectName()) {
|
||||
if len(policy.Projects) > 0 {
|
||||
weight += 1000
|
||||
}
|
||||
if len(policy.Ips) == 0 || containsIp(policy.Ips, userCred.GetLoginIp()) {
|
||||
if len(policy.Ips) > 0 {
|
||||
weight += 10000
|
||||
}
|
||||
return true, weight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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 rbacutils
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
)
|
||||
|
||||
type TPolicy []SRbacRule
|
||||
|
||||
func (policy TPolicy) getMatchRule(req []string) *SRbacRule {
|
||||
service := WILD_MATCH
|
||||
if len(req) > levelService {
|
||||
service = req[levelService]
|
||||
}
|
||||
resource := WILD_MATCH
|
||||
if len(req) > levelResource {
|
||||
resource = req[levelResource]
|
||||
}
|
||||
action := WILD_MATCH
|
||||
if len(req) > levelAction {
|
||||
action = req[levelAction]
|
||||
}
|
||||
var extra []string
|
||||
if len(req) > levelExtra {
|
||||
extra = req[levelExtra:]
|
||||
} else {
|
||||
extra = make([]string, 0)
|
||||
}
|
||||
|
||||
return policy.GetMatchRule(service, resource, action, extra...)
|
||||
}
|
||||
|
||||
func (policy TPolicy) GetMatchRule(service string, resource string, action string, extra ...string) *SRbacRule {
|
||||
return GetMatchRule(policy, service, resource, action, extra...)
|
||||
}
|
||||
|
||||
func DecodePolicy(policyJson jsonutils.JSONObject) (TPolicy, error) {
|
||||
rules, err := json2Rules(policyJson)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "json2Rules")
|
||||
}
|
||||
if len(rules) == 0 {
|
||||
return nil, ErrEmptyPolicy
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func DecodePolicyData(input jsonutils.JSONObject) (TPolicy, error) {
|
||||
policyData, err := input.Get("policy")
|
||||
if err != nil || policyData == nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidFormat, "invalid policy data")
|
||||
}
|
||||
return DecodePolicy(policyData)
|
||||
}
|
||||
|
||||
func (policy TPolicy) Encode() jsonutils.JSONObject {
|
||||
return rules2Json(policy)
|
||||
}
|
||||
|
||||
func (policy TPolicy) EncodeData() jsonutils.JSONObject {
|
||||
ret := jsonutils.NewDict()
|
||||
ret.Add(policy.Encode(), "policy")
|
||||
return ret
|
||||
}
|
||||
|
||||
func (policy TPolicy) Explain(request [][]string) [][]string {
|
||||
output := make([][]string, len(request))
|
||||
for i := 0; i < len(request); i += 1 {
|
||||
rule := policy.getMatchRule(request[i])
|
||||
if rule == nil {
|
||||
output[i] = append(request[i], string(Deny))
|
||||
} else {
|
||||
output[i] = append(request[i], string(rule.Result))
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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 rbacutils
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
)
|
||||
|
||||
/*
|
||||
type SPolicyInfo struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
DomainId string `json:"domain_id"`
|
||||
IsPublic bool `json:"is_public"`
|
||||
PublicScope string `json:"public_scope"`
|
||||
SharedDomainIds []string `json:"shared_domain_ids"`
|
||||
Scope TRbacScope `json:"scope"`
|
||||
Policy *SRbacPolicyCore `json:"policy"`
|
||||
}
|
||||
|
||||
func GetMatchedPolicies(policies []SPolicyInfo, userCred IRbacIdentity) (TPolicySet, []string) {
|
||||
matchedPolicies := make([]*SRbacPolicyCore, 0)
|
||||
matchedNames := make([]string, 0)
|
||||
for i := range policies {
|
||||
isMatched, _ := policies[i].Policy.Match(userCred)
|
||||
if !isMatched {
|
||||
continue
|
||||
}
|
||||
matchedPolicies = append(matchedPolicies, policies[i].Policy)
|
||||
matchedNames = append(matchedNames, policies[i].Name)
|
||||
}
|
||||
return matchedPolicies, matchedNames
|
||||
}*/
|
||||
|
||||
type TPolicyGroup map[TRbacScope]TPolicySet
|
||||
|
||||
func DecodePolicyGroup(json jsonutils.JSONObject) (TPolicyGroup, error) {
|
||||
jmap, err := json.GetMap()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidFormat, "invalid json: not a map")
|
||||
}
|
||||
group := TPolicyGroup{}
|
||||
for k := range jmap {
|
||||
group[TRbacScope(k)], err = DecodePolicySet(jmap[k])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "decode %s", k)
|
||||
}
|
||||
}
|
||||
return group, nil
|
||||
}
|
||||
|
||||
func (sets TPolicyGroup) HighestScope() TRbacScope {
|
||||
for _, s := range []TRbacScope{
|
||||
ScopeSystem,
|
||||
ScopeDomain,
|
||||
ScopeProject,
|
||||
ScopeUser,
|
||||
} {
|
||||
if _, ok := sets[s]; ok {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return ScopeNone
|
||||
}
|
||||
|
||||
func (sets TPolicyGroup) Encode() jsonutils.JSONObject {
|
||||
j := jsonutils.NewDict()
|
||||
for k := range sets {
|
||||
j.Set(string(k), sets[k].Encode())
|
||||
}
|
||||
return j
|
||||
}
|
||||
@@ -14,27 +14,14 @@
|
||||
|
||||
package rbacutils
|
||||
|
||||
type SPolicyInfo struct {
|
||||
Id string
|
||||
Name string
|
||||
Policy *SRbacPolicy
|
||||
}
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
type TPolicySet []*SRbacPolicy
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
)
|
||||
|
||||
func GetMatchedPolicies(policies []SPolicyInfo, userCred IRbacIdentity) (TPolicySet, []string) {
|
||||
matchedPolicies := make([]*SRbacPolicy, 0)
|
||||
matchedNames := make([]string, 0)
|
||||
for i := range policies {
|
||||
isMatched, _ := policies[i].Policy.Match(userCred)
|
||||
if !isMatched {
|
||||
continue
|
||||
}
|
||||
matchedPolicies = append(matchedPolicies, policies[i].Policy)
|
||||
matchedNames = append(matchedNames, policies[i].Name)
|
||||
}
|
||||
return matchedPolicies, matchedNames
|
||||
}
|
||||
type TPolicySet []TPolicy
|
||||
|
||||
func (policies TPolicySet) GetMatchRules(service string, resource string, action string, extra ...string) []SRbacRule {
|
||||
matchRules := make([]SRbacRule, 0)
|
||||
@@ -47,6 +34,30 @@ func (policies TPolicySet) GetMatchRules(service string, resource string, action
|
||||
return matchRules
|
||||
}
|
||||
|
||||
func DecodePolicySet(jsonObj jsonutils.JSONObject) (TPolicySet, error) {
|
||||
jsonArr, err := jsonObj.GetArray()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidFormat, "invalid json: not an array")
|
||||
}
|
||||
set := TPolicySet{}
|
||||
for i := range jsonArr {
|
||||
policy, err := DecodePolicy(jsonArr[i])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "decode %d", i)
|
||||
}
|
||||
set = append(set, policy)
|
||||
}
|
||||
return set, nil
|
||||
}
|
||||
|
||||
func (policies TPolicySet) Encode() jsonutils.JSONObject {
|
||||
obj := make([]jsonutils.JSONObject, len(policies))
|
||||
for i := range policies {
|
||||
obj[i] = policies[i].Encode()
|
||||
}
|
||||
return jsonutils.NewArray(obj...)
|
||||
}
|
||||
|
||||
// ViolatedBy: policies中deny的权限,但是assign中却是allow
|
||||
// if any assign allow, but policies deny
|
||||
// OR
|
||||
@@ -70,9 +81,9 @@ func (policies TPolicySet) violatedBySet(assign TPolicySet, expect TRbacResult)
|
||||
return false
|
||||
}
|
||||
|
||||
func (policies TPolicySet) violatedByPolicy(policy *SRbacPolicy, expect TRbacResult) bool {
|
||||
for i := range policy.Rules {
|
||||
rule := policy.Rules[i]
|
||||
func (policies TPolicySet) violatedByPolicy(policy TPolicy, expect TRbacResult) bool {
|
||||
for i := range policy {
|
||||
rule := policy[i]
|
||||
if rule.Result != expect {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -26,32 +26,28 @@ func TestTPolicySet_Violate(t *testing.T) {
|
||||
{
|
||||
name: "case1",
|
||||
p1: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
p2: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -60,40 +56,34 @@ func TestTPolicySet_Violate(t *testing.T) {
|
||||
{
|
||||
name: "case2",
|
||||
p1: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: "comptue",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: "comptue",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
p2: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -102,60 +92,52 @@ func TestTPolicySet_Violate(t *testing.T) {
|
||||
{
|
||||
name: "case3",
|
||||
p1: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "create",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "create",
|
||||
Result: Deny,
|
||||
},
|
||||
},
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: "comptue",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: "comptue",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
p2: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Deny,
|
||||
},
|
||||
},
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: "comptue",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "get",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: "comptue",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "get",
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -164,30 +146,24 @@ func TestTPolicySet_Violate(t *testing.T) {
|
||||
{
|
||||
name: "case4",
|
||||
p2: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Scope: ScopeDomain,
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
p1: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Scope: ScopeDomain,
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+45
-407
@@ -15,12 +15,9 @@
|
||||
package rbacutils
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
@@ -87,32 +84,6 @@ func (s1 TRbacScope) HigherThan(s2 TRbacScope) bool {
|
||||
return scopeScore[s1] > scopeScore[s2]
|
||||
}
|
||||
|
||||
type SRbacPolicy struct {
|
||||
// condition, when the policy takes effects
|
||||
// Deprecated
|
||||
Condition string
|
||||
|
||||
DomainId string
|
||||
|
||||
IsPublic bool
|
||||
PublicScope TRbacScope
|
||||
SharedDomainIds []string
|
||||
|
||||
Projects []string
|
||||
Roles []string
|
||||
Ips []netutils.IPV4Prefix
|
||||
Auth bool // whether needs authentication
|
||||
|
||||
// scope, the scope of the policy, system/domain/project
|
||||
Scope TRbacScope
|
||||
// Deprecated
|
||||
// is_admin=true means scope=system, is_admin=false means scope=project
|
||||
IsAdmin bool
|
||||
|
||||
// rules, the exact rules
|
||||
Rules []SRbacRule
|
||||
}
|
||||
|
||||
type SRbacRule struct {
|
||||
Service string
|
||||
Resource string
|
||||
@@ -201,33 +172,6 @@ func (rule *SRbacRule) match(service string, resource string, action string, ext
|
||||
return true, matched, weight
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) getMatchRule(req []string) *SRbacRule {
|
||||
service := WILD_MATCH
|
||||
if len(req) > levelService {
|
||||
service = req[levelService]
|
||||
}
|
||||
resource := WILD_MATCH
|
||||
if len(req) > levelResource {
|
||||
resource = req[levelResource]
|
||||
}
|
||||
action := WILD_MATCH
|
||||
if len(req) > levelAction {
|
||||
action = req[levelAction]
|
||||
}
|
||||
var extra []string
|
||||
if len(req) > levelExtra {
|
||||
extra = req[levelExtra:]
|
||||
} else {
|
||||
extra = make([]string, 0)
|
||||
}
|
||||
|
||||
return policy.GetMatchRule(service, resource, action, extra...)
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) GetMatchRule(service string, resource string, action string, extra ...string) *SRbacRule {
|
||||
return GetMatchRule(policy.Rules, service, resource, action, extra...)
|
||||
}
|
||||
|
||||
var (
|
||||
ShowMatchRuleDebug = false
|
||||
)
|
||||
@@ -252,103 +196,6 @@ func GetMatchRule(rules []SRbacRule, service string, resource string, action str
|
||||
return matchRule
|
||||
}
|
||||
|
||||
var (
|
||||
tenantEqualsPattern = regexp.MustCompile(`tenant\s*==\s*['"]?(\w+)['"]?`)
|
||||
roleContainsPattern = regexp.MustCompile(`roles.contains\(['"]?(\w+)['"]?\)`)
|
||||
)
|
||||
|
||||
func searchMatchStrings(pattern *regexp.Regexp, condstr string) []string {
|
||||
ret := make([]string, 0)
|
||||
matches := pattern.FindAllStringSubmatch(condstr, -1)
|
||||
for _, match := range matches {
|
||||
ret = append(ret, match[1])
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func searchMatchTenants(condstr string) []string {
|
||||
return searchMatchStrings(tenantEqualsPattern, condstr)
|
||||
}
|
||||
|
||||
func searchMatchRoles(condstr string) []string {
|
||||
return searchMatchStrings(roleContainsPattern, condstr)
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) Decode(policyJson jsonutils.JSONObject) error {
|
||||
policy.Condition, _ = policyJson.GetString("condition")
|
||||
if policyJson.Contains("projects") {
|
||||
projectJson, _ := policyJson.GetArray("projects")
|
||||
policy.Projects = jsonutils.JSONArray2StringArray(projectJson)
|
||||
}
|
||||
if policyJson.Contains("roles") {
|
||||
roleJson, _ := policyJson.GetArray("roles")
|
||||
policy.Roles = jsonutils.JSONArray2StringArray(roleJson)
|
||||
}
|
||||
|
||||
if len(policy.Projects) == 0 && len(policy.Roles) == 0 && len(policy.Condition) > 0 {
|
||||
// XXX hack
|
||||
// for smooth transtion from condition to projects&roles
|
||||
policy.Projects = searchMatchTenants(policy.Condition)
|
||||
policy.Roles = searchMatchRoles(policy.Condition)
|
||||
}
|
||||
// empty condition, no longer use this field
|
||||
policy.Condition = ""
|
||||
|
||||
scopeStr, _ := policyJson.GetString("scope")
|
||||
if len(scopeStr) > 0 {
|
||||
policy.Scope = TRbacScope(scopeStr)
|
||||
} else {
|
||||
policy.IsAdmin = jsonutils.QueryBoolean(policyJson, "is_admin", false)
|
||||
if len(policy.Scope) == 0 {
|
||||
if policy.IsAdmin {
|
||||
policy.Scope = ScopeSystem
|
||||
} else {
|
||||
policy.Scope = ScopeProject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if policyJson.Contains("ips") {
|
||||
ipsJson, _ := policyJson.GetArray("ips")
|
||||
ipStrs := jsonutils.JSONArray2StringArray(ipsJson)
|
||||
policy.Ips = make([]netutils.IPV4Prefix, 0)
|
||||
for _, ipStr := range ipStrs {
|
||||
if len(ipStr) == 0 || ipStr == "0.0.0.0" {
|
||||
continue
|
||||
}
|
||||
prefix, err := netutils.NewIPV4Prefix(ipStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
policy.Ips = append(policy.Ips, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
policy.Auth = jsonutils.QueryBoolean(policyJson, "auth", true)
|
||||
if len(policy.Ips) > 0 || len(policy.Roles) > 0 || len(policy.Projects) > 0 {
|
||||
policy.Auth = true
|
||||
}
|
||||
|
||||
ruleJson, err := policyJson.Get("policy")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
/*rules, err := decode(ruleJson, SRbacRule{}, levelService)*/
|
||||
rules, err := json2Rules(ruleJson)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "json2Rules")
|
||||
}
|
||||
|
||||
if len(rules) == 0 {
|
||||
return ErrEmptyPolicy
|
||||
}
|
||||
|
||||
policy.Rules = rules
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
levelService = 0
|
||||
levelResource = 1
|
||||
@@ -356,55 +203,6 @@ const (
|
||||
levelExtra = 3
|
||||
)
|
||||
|
||||
func decode(rules jsonutils.JSONObject, decodeRule SRbacRule, level int) ([]SRbacRule, error) {
|
||||
switch rules.(type) {
|
||||
case *jsonutils.JSONString:
|
||||
ruleJsonStr := rules.(*jsonutils.JSONString)
|
||||
ruleStr, _ := ruleJsonStr.GetString()
|
||||
switch ruleStr {
|
||||
case string(Allow), string(AdminAllow), string(OwnerAllow), string(UserAllow), string(GuestAllow):
|
||||
decodeRule.Result = Allow
|
||||
default:
|
||||
decodeRule.Result = Deny
|
||||
// default:
|
||||
// return nil, fmt.Errorf("unsupported rule string %s", ruleStr)
|
||||
}
|
||||
return []SRbacRule{decodeRule}, nil
|
||||
case *jsonutils.JSONDict:
|
||||
ruleJsonDict, err := rules.GetMap()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get rule map fail")
|
||||
}
|
||||
rules := make([]SRbacRule, 0)
|
||||
for key, ruleJson := range ruleJsonDict {
|
||||
rule := decodeRule
|
||||
switch {
|
||||
case level == levelService:
|
||||
rule.Service = key
|
||||
case level == levelResource:
|
||||
rule.Resource = key
|
||||
case level == levelAction:
|
||||
rule.Action = key
|
||||
case level >= levelExtra:
|
||||
if rule.Extra == nil {
|
||||
rule.Extra = make([]string, 1)
|
||||
rule.Extra[0] = key
|
||||
} else {
|
||||
rule.Extra = append(rule.Extra, key)
|
||||
}
|
||||
}
|
||||
decoded, err := decode(ruleJson, rule, level+1)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode")
|
||||
}
|
||||
rules = append(rules, decoded...)
|
||||
}
|
||||
return rules, nil
|
||||
default:
|
||||
return nil, errors.Wrap(ErrUnsuportRuleData, rules.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (rule *SRbacRule) toStringArray() []string {
|
||||
strArr := make([]string, 0)
|
||||
strArr = append(strArr, rule.Service)
|
||||
@@ -420,106 +218,6 @@ func (rule *SRbacRule) toStringArray() []string {
|
||||
return strArr[0 : i+1]
|
||||
}
|
||||
|
||||
func addRule2Json(nodeJson *jsonutils.JSONDict, keys []string, result TRbacResult) error {
|
||||
if len(keys) == 1 {
|
||||
if nodeJson.Contains(keys[0]) {
|
||||
nextJson, _ := nodeJson.Get(keys[0])
|
||||
switch nextJson.(type) {
|
||||
case *jsonutils.JSONString: // conflict??
|
||||
return ErrConflict // fmt.Errorf("conflict?")
|
||||
case *jsonutils.JSONDict:
|
||||
nextJsonDict := nextJson.(*jsonutils.JSONDict)
|
||||
addRule2Json(nextJsonDict, []string{WILD_MATCH}, result)
|
||||
return nil
|
||||
default:
|
||||
return ErrInvalidRules // fmt.Errorf("invalid rules")
|
||||
}
|
||||
} else {
|
||||
nodeJson.Add(jsonutils.NewString(string(result)), keys[0])
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// len(keys) > 1
|
||||
exist, _ := nodeJson.Get(keys[0])
|
||||
if exist != nil {
|
||||
switch exist.(type) {
|
||||
case *jsonutils.JSONString: // need restruct
|
||||
newDict := jsonutils.NewDict()
|
||||
newDict.Add(exist, "*")
|
||||
nodeJson.Set(keys[0], newDict)
|
||||
return addRule2Json(newDict, keys[1:], result)
|
||||
case *jsonutils.JSONDict:
|
||||
existDict := exist.(*jsonutils.JSONDict)
|
||||
return addRule2Json(existDict, keys[1:], result)
|
||||
default:
|
||||
return ErrInvalidRules // fmt.Errorf("invalid rules")
|
||||
}
|
||||
} else {
|
||||
next := jsonutils.NewDict()
|
||||
nodeJson.Add(next, keys[0])
|
||||
return addRule2Json(next, keys[1:], result)
|
||||
}
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) Encode() (jsonutils.JSONObject, error) {
|
||||
/*rules := jsonutils.NewDict()
|
||||
for i := 0; i < len(policy.Rules); i += 1 {
|
||||
keys := policy.Rules[i].toStringArray()
|
||||
err := addRule2Json(rules, keys, policy.Rules[i].Result)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "addRule2Json")
|
||||
}
|
||||
}*/
|
||||
|
||||
rules := rules2Json(policy.Rules)
|
||||
|
||||
ret := jsonutils.NewDict()
|
||||
// ret.Add(jsonutils.NewString(policy.Condition), "condition")
|
||||
// if policy.IsAdmin {
|
||||
// ret.Add(jsonutils.JSONTrue, "is_admin")
|
||||
// } else {
|
||||
// ret.Add(jsonutils.JSONFalse, "is_admin")
|
||||
// }
|
||||
|
||||
ret.Add(jsonutils.NewString(string(policy.Scope)), "scope")
|
||||
|
||||
if !policy.Auth && len(policy.Projects) == 0 && len(policy.Roles) == 0 && len(policy.Ips) == 0 {
|
||||
ret.Add(jsonutils.JSONFalse, "auth")
|
||||
} else {
|
||||
ret.Add(jsonutils.JSONTrue, "auth")
|
||||
}
|
||||
|
||||
if len(policy.Projects) > 0 {
|
||||
ret.Add(jsonutils.NewStringArray(policy.Projects), "projects")
|
||||
}
|
||||
if len(policy.Roles) > 0 {
|
||||
ret.Add(jsonutils.NewStringArray(policy.Roles), "roles")
|
||||
}
|
||||
if len(policy.Ips) > 0 {
|
||||
ipStrs := make([]string, len(policy.Ips))
|
||||
for i := range policy.Ips {
|
||||
ipStrs[i] = policy.Ips[i].String()
|
||||
}
|
||||
ret.Add(jsonutils.NewStringArray(ipStrs), "ips")
|
||||
}
|
||||
|
||||
ret.Add(rules, "policy")
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) Explain(request [][]string) [][]string {
|
||||
output := make([][]string, len(request))
|
||||
for i := 0; i < len(request); i += 1 {
|
||||
rule := policy.getMatchRule(request[i])
|
||||
if rule == nil {
|
||||
output[i] = append(request[i], string(Deny))
|
||||
} else {
|
||||
output[i] = append(request[i], string(rule.Result))
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func contains(s1 []string, s string) bool {
|
||||
for i := range s1 {
|
||||
if s1[i] == s {
|
||||
@@ -558,7 +256,18 @@ func containsIp(ips []netutils.IPV4Prefix, ipStr string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
FAKE_TOKEN = "fake_token"
|
||||
)
|
||||
|
||||
type IRbacIdentity interface {
|
||||
GetProjectId() string
|
||||
GetRoleIds() []string
|
||||
GetLoginIp() string
|
||||
GetTokenString() string
|
||||
}
|
||||
|
||||
type IRbacIdentity2 interface {
|
||||
GetProjectDomainId() string
|
||||
GetProjectName() string
|
||||
GetRoles() []string
|
||||
@@ -566,128 +275,57 @@ type IRbacIdentity interface {
|
||||
GetTokenString() string
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) IsSystemWidePolicy() bool {
|
||||
return (len(policy.DomainId) == 0 || (policy.IsPublic && policy.PublicScope == ScopeSystem)) && len(policy.Roles) == 0 && len(policy.Projects) == 0
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchDomain(domainId string) bool {
|
||||
if len(policy.DomainId) == 0 || len(domainId) == 0 {
|
||||
return true
|
||||
}
|
||||
if policy.DomainId == domainId {
|
||||
return true
|
||||
}
|
||||
if policy.IsPublic {
|
||||
if policy.PublicScope == ScopeSystem {
|
||||
return true
|
||||
}
|
||||
if contains(policy.SharedDomainIds, domainId) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchProject(projectName string) bool {
|
||||
if len(policy.Projects) == 0 || len(projectName) == 0 {
|
||||
return true
|
||||
}
|
||||
if contains(policy.Projects, projectName) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchRoles(roleNames []string) bool {
|
||||
if len(policy.Roles) == 0 {
|
||||
return true
|
||||
}
|
||||
if intersect(policy.Roles, roleNames) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// check whether policy maches a userCred
|
||||
// return value
|
||||
// bool isMatched
|
||||
// int match weight, the higher the value, the more exact the match
|
||||
// the more exact match wins
|
||||
func (policy *SRbacPolicy) Match(userCred IRbacIdentity) (bool, int) {
|
||||
if !policy.Auth && len(policy.Roles) == 0 && len(policy.Projects) == 0 && len(policy.Ips) == 0 {
|
||||
return true, 1
|
||||
}
|
||||
if userCred == nil || len(userCred.GetTokenString()) == 0 {
|
||||
return false, 0
|
||||
}
|
||||
weight := 0
|
||||
if policy.MatchDomain(userCred.GetProjectDomainId()) {
|
||||
if len(policy.DomainId) > 0 {
|
||||
if policy.DomainId == userCred.GetProjectDomainId() {
|
||||
weight += 30 // exact domain match
|
||||
} else if len(policy.SharedDomainIds) > 0 {
|
||||
weight += 20 // shared domain match
|
||||
} else {
|
||||
weight += 10 // else, system scope match
|
||||
}
|
||||
}
|
||||
if policy.MatchRoles(userCred.GetRoles()) {
|
||||
if len(policy.Roles) != 0 {
|
||||
weight += 100
|
||||
}
|
||||
if policy.MatchProject(userCred.GetProjectName()) {
|
||||
if len(policy.Projects) > 0 {
|
||||
weight += 1000
|
||||
}
|
||||
if len(policy.Ips) == 0 || containsIp(policy.Ips, userCred.GetLoginIp()) {
|
||||
if len(policy.Ips) > 0 {
|
||||
weight += 10000
|
||||
}
|
||||
return true, weight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
|
||||
type sSimpleRbacIdentity struct {
|
||||
domainId string
|
||||
projectName string
|
||||
roleNames []string
|
||||
loginIp string
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetProjectDomainId() string {
|
||||
return id.domainId
|
||||
projectDomainId string
|
||||
projectId string
|
||||
projectName string
|
||||
roleIds []string
|
||||
roles []string
|
||||
ip string
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetRoles() []string {
|
||||
return id.roleNames
|
||||
return id.roles
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetRoleIds() []string {
|
||||
return id.roleIds
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetProjectName() string {
|
||||
return id.projectName
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetProjectId() string {
|
||||
return id.projectId
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetProjectDomainId() string {
|
||||
return id.projectDomainId
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetLoginIp() string {
|
||||
return id.loginIp
|
||||
return id.ip
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetTokenString() string {
|
||||
return "faketoken"
|
||||
return FAKE_TOKEN
|
||||
}
|
||||
|
||||
func NewRbacIdentity(domainId, projectName string, roleNames []string) IRbacIdentity {
|
||||
return NewRbacIdentity2(domainId, projectName, roleNames, "")
|
||||
}
|
||||
|
||||
func NewRbacIdentity2(domainId, projectName string, roleNames []string, loginIp string) IRbacIdentity {
|
||||
func newRbacIdentity2(projectDomainId, projectName string, roles []string, ip string) IRbacIdentity2 {
|
||||
return sSimpleRbacIdentity{
|
||||
domainId: domainId,
|
||||
projectName: projectName,
|
||||
roleNames: roleNames,
|
||||
loginIp: loginIp,
|
||||
projectDomainId: projectDomainId,
|
||||
projectName: projectName,
|
||||
roles: roles,
|
||||
ip: ip,
|
||||
}
|
||||
}
|
||||
|
||||
func NewRbacIdentity(projectId string, roleIds []string, ip string) IRbacIdentity {
|
||||
return sSimpleRbacIdentity{
|
||||
projectId: projectId,
|
||||
roleIds: roleIds,
|
||||
ip: ip,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -203,11 +203,7 @@ func TestSRabcPolicy_Encode(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
policyJson1, err := policy.Encode()
|
||||
if err != nil {
|
||||
t.Errorf("encode error %s", err)
|
||||
return
|
||||
}
|
||||
policyJson1 := policy.Encode()
|
||||
|
||||
policy2 := SRbacPolicy{}
|
||||
|
||||
@@ -217,11 +213,7 @@ func TestSRabcPolicy_Encode(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
policyJson2, err := policy2.Encode()
|
||||
if err != nil {
|
||||
t.Errorf("encode error 2 %s", err)
|
||||
return
|
||||
}
|
||||
policyJson2 := policy2.Encode()
|
||||
|
||||
policyStr1 := policyJson1.PrettyString()
|
||||
policyStr2 := policyJson2.PrettyString()
|
||||
@@ -235,6 +227,7 @@ func TestSRabcPolicy_Encode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestSRabcPolicy_Explain(t *testing.T) {
|
||||
policyStr := `{
|
||||
"condition": "usercred.project != \"system\" && usercred.roles==\"projectowner\"",
|
||||
@@ -282,6 +275,7 @@ func TestSRabcPolicy_Explain(t *testing.T) {
|
||||
|
||||
t.Logf("%#v", output)
|
||||
}
|
||||
*/
|
||||
|
||||
func TestConditionParser(t *testing.T) {
|
||||
condition := `tenant=="system" && roles.contains("admin")`
|
||||
@@ -291,44 +285,16 @@ func TestConditionParser(t *testing.T) {
|
||||
t.Logf("%s", roles)
|
||||
}
|
||||
|
||||
type sRbacIdentity struct {
|
||||
DomainId string
|
||||
Project string
|
||||
Roles []string
|
||||
Ip string
|
||||
Token string
|
||||
}
|
||||
|
||||
func (ri *sRbacIdentity) GetProjectDomainId() string {
|
||||
return ri.DomainId
|
||||
}
|
||||
|
||||
func (ri *sRbacIdentity) GetProjectName() string {
|
||||
return ri.Project
|
||||
}
|
||||
|
||||
func (ri *sRbacIdentity) GetRoles() []string {
|
||||
return ri.Roles
|
||||
}
|
||||
|
||||
func (ri *sRbacIdentity) GetLoginIp() string {
|
||||
return ri.Ip
|
||||
}
|
||||
|
||||
func (ri *sRbacIdentity) GetTokenString() string {
|
||||
return ri.Token
|
||||
}
|
||||
|
||||
func TestSRbacPolicyMatch(t *testing.T) {
|
||||
prefix, _ := netutils.NewIPV4Prefix("10.168.22.0/24")
|
||||
cases := []struct {
|
||||
policy SRbacPolicy
|
||||
userCred IRbacIdentity
|
||||
userCred IRbacIdentity2
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
SRbacPolicy{},
|
||||
&sRbacIdentity{},
|
||||
newRbacIdentity2("", "", nil, ""),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -340,20 +306,14 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
SRbacPolicy{
|
||||
Projects: []string{"system"},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", nil, ""),
|
||||
true,
|
||||
},
|
||||
{
|
||||
SRbacPolicy{
|
||||
Projects: []string{"system"},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "demo",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "demo", nil, ""),
|
||||
false,
|
||||
},
|
||||
{
|
||||
@@ -361,11 +321,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Projects: []string{"system"},
|
||||
Roles: []string{"admin"},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"admin"},
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"admin"}, ""),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -373,11 +329,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Projects: []string{"system"},
|
||||
Roles: []string{"admin"},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"admin", "_member_"},
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"admin", "_member_"}, ""),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -385,11 +337,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Projects: []string{"system"},
|
||||
Roles: []string{"admin"},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"_member_"},
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"_member_"}, ""),
|
||||
false,
|
||||
},
|
||||
{
|
||||
@@ -413,12 +361,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"admin"},
|
||||
Ips: []netutils.IPV4Prefix{prefix},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"admin"},
|
||||
Ip: "10.0.0.23",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"admin"}, "10.0.0.23"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
@@ -427,12 +370,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"admin"},
|
||||
Ips: []netutils.IPV4Prefix{prefix},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"admin"},
|
||||
Ip: "10.168.22.23",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"admin"}, "10.168.22.23"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -441,12 +379,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"admin"},
|
||||
Ips: []netutils.IPV4Prefix{prefix},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"_member_"},
|
||||
Ip: "10.168.22.23",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"_member_"}, "10.168.22.23"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
@@ -454,12 +387,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"admin"},
|
||||
Ips: []netutils.IPV4Prefix{prefix},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"_member_", "admin"},
|
||||
Ip: "10.168.22.23",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"_member_", "admin"}, "10.168.22.23"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -468,12 +396,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"admin", "_member_"},
|
||||
Ips: []netutils.IPV4Prefix{prefix},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"_member_", "projectowner"},
|
||||
Ip: "10.168.22.23",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"_member_", "projectowner"}, "10.168.22.23"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -482,11 +405,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"domain_admin"},
|
||||
Auth: true,
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "ldapproj",
|
||||
Roles: []string{"domain_admin"},
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "ldapproj", []string{"domain_admin"}, ""),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -495,7 +414,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"admin"},
|
||||
Auth: true,
|
||||
},
|
||||
NewRbacIdentity("", "", []string{"admin"}),
|
||||
newRbacIdentity2("", "", []string{"admin"}, ""),
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user