feature: policy group support

This commit is contained in:
Qiu Jian
2020-09-27 20:45:23 +08:00
parent 167c672194
commit 2d39cb4cb0
53 changed files with 2306 additions and 972 deletions
@@ -1,6 +0,0 @@
# rbac for project owner, not allow for delete
roles:
- domain_admin
scope: domain
policy:
'*': allow
@@ -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
+18 -20
View File
@@ -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
})
}
+63
View File
@@ -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
})
}
+43 -29
View File
@@ -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
View File
@@ -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
}
+8 -9
View File
@@ -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 {
+22
View File
@@ -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 {
+10 -4
View File
@@ -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"`
}
+65
View File
@@ -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"`
}
-1
View File
@@ -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,
)
}
+2 -2
View File
@@ -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
}
+5 -5
View File
@@ -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()
+4 -4
View File
@@ -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"`
+2 -2
View File
@@ -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)
}
}
+171 -89
View File
@@ -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
}
*/
+4 -4
View File
@@ -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 {
+12 -10
View File
@@ -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)
+3 -1
View File
@@ -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
}
+2 -2
View File
@@ -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
View File
@@ -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
}
+9 -11
View File
@@ -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")
}
+451
View File
@@ -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
}
+178 -5
View File
@@ -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)
}
+2 -2
View File
@@ -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)
+4 -4
View File
@@ -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)
}
+1
View File
@@ -89,6 +89,7 @@ func InitHandlers(app *appsrv.Application) {
models.CredentialManager,
models.IdentityProviderManager,
models.ServiceCertificateManager,
models.RolePolicyManager,
} {
db.RegisterModelManager(manager)
handler := db.NewModelHandler(manager)
+8 -45
View File
@@ -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
}
+1
View File
@@ -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 {
+39
View File
@@ -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())
}
+8 -4
View File
@@ -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()
+10 -2
View File
@@ -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
}
+29
View File
@@ -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)
}
+46
View File
@@ -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
}
+12 -14
View File
@@ -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)
}
+67
View File
@@ -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)
}
+1
View File
@@ -74,6 +74,7 @@ type TokenCredential interface {
GetTokenString() string
GetRoles() []string
GetRoleIds() []string
GetExpires() time.Time
IsValid() bool
ValidDuration() time.Duration
+10
View File
@@ -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
}
+8
View File
@@ -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
}
+6
View File
@@ -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(),
+62
View File
@@ -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
}
+57
View File
@@ -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)
}
}
}
+257
View File
@@ -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
}
+93
View File
@@ -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
}
+88
View File
@@ -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
}
+33 -22
View File
@@ -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
}
+92 -116
View File
@@ -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
View File
@@ -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,
}
}
+18 -99
View File
@@ -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,
},
}