mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-01 15:07:17 +08:00
Merge pull request #2477 from swordqiu/hotfix/qj-policy-match-weight-support
fix: use policy match weight to override less specific policy
This commit is contained in:
@@ -1,3 +1,17 @@
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
|
||||
@@ -38,13 +38,13 @@ import (
|
||||
const (
|
||||
PolicyDelegation = "delegate"
|
||||
|
||||
PolicyActionList = "list"
|
||||
PolicyActionGet = "get"
|
||||
PolicyActionUpdate = "update"
|
||||
PolicyActionPatch = "patch"
|
||||
PolicyActionCreate = "create"
|
||||
PolicyActionDelete = "delete"
|
||||
PolicyActionPerform = "perform"
|
||||
PolicyActionList = rbacutils.ActionList
|
||||
PolicyActionGet = rbacutils.ActionGet
|
||||
PolicyActionUpdate = rbacutils.ActionUpdate
|
||||
PolicyActionPatch = rbacutils.ActionPatch
|
||||
PolicyActionCreate = rbacutils.ActionCreate
|
||||
PolicyActionDelete = rbacutils.ActionDelete
|
||||
PolicyActionPerform = rbacutils.ActionPerform
|
||||
)
|
||||
|
||||
type PolicyFetchFunc func() (map[rbacutils.TRbacScope]map[string]*rbacutils.SRbacPolicy, error)
|
||||
@@ -307,25 +307,53 @@ func (manager *SPolicyManager) findPolicyByName(scope rbacutils.TRbacScope, name
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMatchedPolicyNames(policies map[string]*rbacutils.SRbacPolicy, userCred rbacutils.IRbacIdentity) []string {
|
||||
matchNames := make([]string, 0)
|
||||
maxMatchWeight := 0
|
||||
for k := range policies {
|
||||
isMatched, matchWeight := policies[k].Match(userCred)
|
||||
if !isMatched || matchWeight < maxMatchWeight {
|
||||
continue
|
||||
}
|
||||
if maxMatchWeight < matchWeight {
|
||||
maxMatchWeight = matchWeight
|
||||
matchNames = matchNames[:0]
|
||||
}
|
||||
matchNames = append(matchNames, k)
|
||||
}
|
||||
return matchNames
|
||||
}
|
||||
|
||||
func getMatchedPolicyRules(policies map[string]*rbacutils.SRbacPolicy, userCred rbacutils.IRbacIdentity, service string, resource string, action string, extra ...string) ([]rbacutils.SRbacRule, bool) {
|
||||
matchRules := make([]rbacutils.SRbacRule, 0)
|
||||
findMatchPolicy := false
|
||||
maxMatchWeight := 0
|
||||
for k := range policies {
|
||||
isMatched, matchWeight := policies[k].Match(userCred)
|
||||
if !isMatched || matchWeight < maxMatchWeight {
|
||||
continue
|
||||
}
|
||||
if maxMatchWeight < matchWeight {
|
||||
maxMatchWeight = matchWeight
|
||||
matchRules = matchRules[:0]
|
||||
}
|
||||
findMatchPolicy = true
|
||||
rule := policies[k].GetMatchRule(service, resource, action, extra...)
|
||||
if rule != nil {
|
||||
matchRules = append(matchRules, *rule)
|
||||
}
|
||||
}
|
||||
return matchRules, findMatchPolicy
|
||||
}
|
||||
|
||||
func (manager *SPolicyManager) allowWithoutCache(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 {
|
||||
log.Warningf("no policies fetched for scope %s", scope)
|
||||
} else {
|
||||
for i := range policies {
|
||||
if !policies[i].Match(userCred) {
|
||||
continue
|
||||
}
|
||||
findMatchPolicy = true
|
||||
rule := policies[i].GetMatchRule(service, resource, action, extra...)
|
||||
if rule != nil {
|
||||
matchRules = append(matchRules, *rule)
|
||||
}
|
||||
}
|
||||
matchRules, findMatchPolicy = getMatchedPolicyRules(policies, userCred, service, resource, action, extra...)
|
||||
}
|
||||
|
||||
scopedDeny := false
|
||||
@@ -358,7 +386,8 @@ func (manager *SPolicyManager) allowWithoutCache(scope rbacutils.TRbacScope, use
|
||||
defaultPolicies, ok := manager.defaultPolicies[scope]
|
||||
if ok {
|
||||
for i := range defaultPolicies {
|
||||
if !defaultPolicies[i].Match(userCred) {
|
||||
isMatched, _ := defaultPolicies[i].Match(userCred)
|
||||
if !isMatched {
|
||||
continue
|
||||
}
|
||||
rule := defaultPolicies[i].GetMatchRule(service, resource, action, extra...)
|
||||
@@ -476,10 +505,9 @@ func (manager *SPolicyManager) IsScopeCapable(userCred mcclient.TokenCredential,
|
||||
}
|
||||
|
||||
if policies, ok := manager.policies[scope]; ok {
|
||||
for _, p := range policies {
|
||||
if p.Match(userCred) {
|
||||
return true
|
||||
}
|
||||
pnames := getMatchedPolicyNames(policies, userCred)
|
||||
if len(pnames) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
@@ -491,12 +519,7 @@ func (manager *SPolicyManager) MatchedPolicies(scope rbacutils.TRbacScope, userC
|
||||
if !ok {
|
||||
return ret
|
||||
}
|
||||
for k, p := range policies {
|
||||
if p.Match(userCred) {
|
||||
ret = append(ret, k)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
return getMatchedPolicyNames(policies, userCred)
|
||||
}
|
||||
|
||||
func (manager *SPolicyManager) AllPolicies() map[string][]string {
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// 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
|
||||
|
||||
type ConfigsManager struct {
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// 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 (
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
const (
|
||||
ActionList = "list"
|
||||
ActionGet = "get"
|
||||
ActionUpdate = "update"
|
||||
ActionCreate = "create"
|
||||
ActionDelete = "delete"
|
||||
ActionPerform = "perform"
|
||||
|
||||
ActionPatch = "patch"
|
||||
)
|
||||
|
||||
var (
|
||||
AllActions = []string{
|
||||
ActionList,
|
||||
ActionGet,
|
||||
ActionUpdate,
|
||||
ActionCreate,
|
||||
ActionDelete,
|
||||
ActionPerform,
|
||||
}
|
||||
|
||||
AllSortedActions = stringutils2.NewSortedStrings(AllActions)
|
||||
)
|
||||
+62
-18
@@ -76,6 +76,10 @@ func (r1 TRbacResult) StricterThan(r2 TRbacResult) bool {
|
||||
return r1.Strictness() < r2.Strictness()
|
||||
}
|
||||
|
||||
func (r1 TRbacResult) LooserThan(r2 TRbacResult) bool {
|
||||
return r1.Strictness() > r2.Strictness()
|
||||
}
|
||||
|
||||
func (s1 TRbacScope) HigherEqual(s2 TRbacScope) bool {
|
||||
return scopeScore[s1] >= scopeScore[s2]
|
||||
}
|
||||
@@ -113,6 +117,15 @@ type SRbacRule struct {
|
||||
Result TRbacResult
|
||||
}
|
||||
|
||||
func (r SRbacRule) clone() SRbacRule {
|
||||
nr := r
|
||||
nr.Extra = make([]string, len(r.Extra))
|
||||
if len(r.Extra) > 0 {
|
||||
copy(nr.Extra, r.Extra)
|
||||
}
|
||||
return nr
|
||||
}
|
||||
|
||||
func isWildMatch(str string) bool {
|
||||
return len(str) == 0 || str == WILD_MATCH
|
||||
}
|
||||
@@ -144,6 +157,10 @@ func (rule *SRbacRule) stricterThan(r2 *SRbacRule) bool {
|
||||
return rule.Result.StricterThan(r2.Result)
|
||||
}
|
||||
|
||||
func (rule *SRbacRule) looserThan(r2 *SRbacRule) bool {
|
||||
return rule.Result.LooserThan(r2.Result)
|
||||
}
|
||||
|
||||
func (rule *SRbacRule) match(service string, resource string, action string, extra ...string) (bool, int, int) {
|
||||
matched := 0
|
||||
weight := 0
|
||||
@@ -215,7 +232,7 @@ func GetMatchRule(rules []SRbacRule, service string, resource string, action str
|
||||
match, matchCnt, weight := rules[i].match(service, resource, action, extra...)
|
||||
if match && (maxMatchCnt < matchCnt ||
|
||||
(maxMatchCnt == matchCnt && minWeight > weight) ||
|
||||
(maxMatchCnt == matchCnt && minWeight == weight && matchRule.stricterThan(&rules[i]))) {
|
||||
(maxMatchCnt == matchCnt && minWeight == weight && matchRule.looserThan(&rules[i]))) {
|
||||
maxMatchCnt = matchCnt
|
||||
minWeight = weight
|
||||
matchRule = &rules[i]
|
||||
@@ -228,7 +245,7 @@ func CompactRules(rules []SRbacRule) []SRbacRule {
|
||||
if len(rules) == 0 {
|
||||
return nil
|
||||
}
|
||||
output := make([]SRbacRule, 1)
|
||||
/*output := make([]SRbacRule, 1)
|
||||
output[0] = rules[0]
|
||||
for i := 1; i < len(rules); i += 1 {
|
||||
isContains := false
|
||||
@@ -246,8 +263,8 @@ func CompactRules(rules []SRbacRule) []SRbacRule {
|
||||
if !isContains {
|
||||
output = append(output, rules[i])
|
||||
}
|
||||
}
|
||||
return output
|
||||
}*/
|
||||
return reduceRules(rules)
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -332,9 +349,10 @@ func (policy *SRbacPolicy) Decode(policyJson jsonutils.JSONObject) error {
|
||||
return err
|
||||
}
|
||||
|
||||
rules, err := decode(ruleJson, SRbacRule{}, levelService)
|
||||
/*rules, err := decode(ruleJson, SRbacRule{}, levelService)*/
|
||||
rules, err := json2Rules(ruleJson)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "json2Rules")
|
||||
}
|
||||
|
||||
if len(rules) == 0 {
|
||||
@@ -459,14 +477,16 @@ func addRule2Json(nodeJson *jsonutils.JSONDict, keys []string, result TRbacResul
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) Encode() (jsonutils.JSONObject, error) {
|
||||
rules := jsonutils.NewDict()
|
||||
/*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")
|
||||
@@ -564,20 +584,44 @@ func (policy *SRbacPolicy) IsSystemWidePolicy() bool {
|
||||
return len(policy.Roles) == 0 && len(policy.Projects) == 0
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) Match(userCred IRbacIdentity) bool {
|
||||
if !policy.Auth && len(policy.Projects) == 0 && len(policy.Roles) == 0 && len(policy.Ips) == 0 {
|
||||
return true
|
||||
// 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 {
|
||||
return false
|
||||
return false, 0
|
||||
}
|
||||
if !policy.IsPublic && len(policy.DomainId) > 0 && policy.DomainId != userCred.GetProjectDomainId() {
|
||||
return false
|
||||
weight := 0
|
||||
if policy.IsPublic || len(policy.DomainId) == 0 || policy.DomainId == userCred.GetProjectDomainId() {
|
||||
if len(policy.DomainId) > 0 {
|
||||
weight += 10
|
||||
}
|
||||
if !policy.IsPublic {
|
||||
weight += 10
|
||||
}
|
||||
if len(policy.Roles) == 0 || intersect(policy.Roles, userCred.GetRoles()) {
|
||||
if len(policy.Roles) != 0 {
|
||||
weight += 100
|
||||
}
|
||||
if len(policy.Projects) == 0 || contains(policy.Projects, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (len(policy.Projects) == 0 || contains(policy.Projects, userCred.GetProjectName())) && (len(policy.Roles) == 0 || intersect(policy.Roles, userCred.GetRoles())) && (len(policy.Ips) == 0 || containsIp(policy.Ips, userCred.GetLoginIp())) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return false, 0
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchRole(roleName string) bool {
|
||||
|
||||
@@ -475,10 +475,10 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := c.policy.Match(c.userCred)
|
||||
for i, c := range cases {
|
||||
got, _ := c.policy.Match(c.userCred)
|
||||
if got != c.want {
|
||||
t.Errorf("%#v %#v got %v want %v", c.policy, c.userCred, got, c.want)
|
||||
t.Errorf("[%d]: %#v %#v got %v want %v", i, c.policy, c.userCred, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
// 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 (
|
||||
"sort"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
type sRbacNode struct {
|
||||
defNode *sRbacNode
|
||||
downStream map[string]*sRbacNode
|
||||
result *TRbacResult
|
||||
level int
|
||||
}
|
||||
|
||||
func newRbacNode(level int) *sRbacNode {
|
||||
return &sRbacNode{
|
||||
downStream: make(map[string]*sRbacNode),
|
||||
level: level,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *sRbacNode) AddRule(rule SRbacRule) {
|
||||
n.addRule(rule, levelService)
|
||||
}
|
||||
|
||||
func (n *sRbacNode) addRule(rule SRbacRule, level int) {
|
||||
if level <= levelAction || len(rule.Extra) > level-levelExtra {
|
||||
if n.result != nil {
|
||||
// node is a leaf
|
||||
if n.defNode != nil {
|
||||
log.Fatalf("illegal state, result != nil and defNode != nil")
|
||||
}
|
||||
n.defNode = newRbacNode(n.level + 1)
|
||||
n.defNode.result = n.result
|
||||
n.result = nil
|
||||
}
|
||||
var key string
|
||||
if level == levelService {
|
||||
key = rule.Service
|
||||
} else if level == levelResource {
|
||||
key = rule.Resource
|
||||
} else if level == levelAction {
|
||||
key = rule.Action
|
||||
} else {
|
||||
key = rule.Extra[level-levelExtra]
|
||||
}
|
||||
if len(key) == 0 || key == WILD_MATCH {
|
||||
next := n.defNode
|
||||
if n.defNode == nil {
|
||||
next = newRbacNode(level + 1)
|
||||
n.defNode = next
|
||||
}
|
||||
next.addRule(rule, level+1)
|
||||
} else {
|
||||
next, ok := n.downStream[key]
|
||||
if !ok {
|
||||
next = newRbacNode(level + 1)
|
||||
n.downStream[key] = next
|
||||
}
|
||||
next.addRule(rule, level+1)
|
||||
}
|
||||
} else {
|
||||
if n.result != nil {
|
||||
log.Warningf("node has been occupide!!!")
|
||||
}
|
||||
n.result = &rule.Result
|
||||
}
|
||||
}
|
||||
|
||||
func (n *sRbacNode) isLeaf() bool {
|
||||
return n.result != nil && n.defNode == nil && len(n.downStream) == 0
|
||||
}
|
||||
|
||||
func (n *sRbacNode) reduceDownstream() {
|
||||
allowKey := make([]string, 0)
|
||||
denyKey := make([]string, 0)
|
||||
skipKey := make([]string, 0)
|
||||
for k, v := range n.downStream {
|
||||
if v.result == nil {
|
||||
skipKey = append(skipKey, k)
|
||||
continue
|
||||
}
|
||||
if *v.result == Allow {
|
||||
allowKey = append(allowKey, k)
|
||||
} else {
|
||||
denyKey = append(denyKey, k)
|
||||
}
|
||||
}
|
||||
if len(allowKey)+len(denyKey) > 0 {
|
||||
var result TRbacResult
|
||||
var keys []string
|
||||
if n.defNode != nil {
|
||||
if n.defNode.isLeaf() {
|
||||
if *n.defNode.result == Allow {
|
||||
keys = allowKey
|
||||
result = Allow
|
||||
} else {
|
||||
keys = denyKey
|
||||
result = Deny
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if len(allowKey) >= len(denyKey) {
|
||||
keys = allowKey
|
||||
result = Allow
|
||||
} else {
|
||||
keys = denyKey
|
||||
result = Deny
|
||||
}
|
||||
if len(allowKey) == 0 || len(denyKey) == 0 {
|
||||
// reduce whole downStream
|
||||
needBranch := false
|
||||
if n.level == levelAction {
|
||||
sort.Strings(keys)
|
||||
if !stringutils2.Contains(stringutils2.SSortedStrings(keys), AllSortedActions) {
|
||||
// not a complete set of action, cancel reduce
|
||||
needBranch = true
|
||||
}
|
||||
} else if n.level >= levelExtra {
|
||||
if len(keys) <= 1 {
|
||||
needBranch = true
|
||||
}
|
||||
}
|
||||
|
||||
if needBranch {
|
||||
keys = nil
|
||||
if result == Allow {
|
||||
result = Deny
|
||||
} else {
|
||||
result = Allow
|
||||
}
|
||||
// add a default branch
|
||||
n.defNode = newRbacNode(n.level + 1)
|
||||
n.defNode.result = &result
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
for _, k := range keys {
|
||||
delete(n.downStream, k)
|
||||
}
|
||||
if n.defNode == nil {
|
||||
n.defNode = newRbacNode(n.level + 1)
|
||||
}
|
||||
n.defNode.result = &result
|
||||
}
|
||||
}
|
||||
if len(n.downStream) == 0 && n.defNode != nil && n.defNode.isLeaf() {
|
||||
n.result = n.defNode.result
|
||||
n.defNode = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *sRbacNode) reduce() {
|
||||
if n.defNode != nil {
|
||||
n.defNode.reduce()
|
||||
}
|
||||
for k := range n.downStream {
|
||||
n.downStream[k].reduce()
|
||||
}
|
||||
n.reduceDownstream()
|
||||
}
|
||||
|
||||
func (n *sRbacNode) GetRules() []SRbacRule {
|
||||
return n.getRules(SRbacRule{Service: WILD_MATCH}, levelService)
|
||||
}
|
||||
|
||||
func (n *sRbacNode) getRules(seed SRbacRule, level int) []SRbacRule {
|
||||
result := make([]SRbacRule, 0)
|
||||
if n.result != nil {
|
||||
rule := seed.clone()
|
||||
rule.Result = *n.result
|
||||
return []SRbacRule{rule}
|
||||
} else {
|
||||
if n.defNode != nil {
|
||||
rule := seed.clone()
|
||||
if level == levelService {
|
||||
rule.Service = WILD_MATCH
|
||||
} else if level == levelResource {
|
||||
rule.Resource = WILD_MATCH
|
||||
} else if level == levelAction {
|
||||
rule.Action = WILD_MATCH
|
||||
} else if level >= levelExtra {
|
||||
rule.Extra = append(rule.Extra, WILD_MATCH)
|
||||
}
|
||||
newRules := n.defNode.getRules(rule, level+1)
|
||||
result = append(result, newRules...)
|
||||
}
|
||||
for k := range n.downStream {
|
||||
rule := seed.clone()
|
||||
if level == levelService {
|
||||
rule.Service = k
|
||||
} else if level == levelResource {
|
||||
rule.Resource = k
|
||||
} else if level == levelAction {
|
||||
rule.Action = k
|
||||
} else if level >= levelExtra {
|
||||
rule.Extra = append(rule.Extra, k)
|
||||
}
|
||||
newRules := n.downStream[k].getRules(rule, level+1)
|
||||
result = append(result, newRules...)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func reduceRules(rules []SRbacRule) []SRbacRule {
|
||||
root := newRbacNode(levelService)
|
||||
for _, r := range rules {
|
||||
root.AddRule(r)
|
||||
}
|
||||
root.reduce()
|
||||
return root.GetRules()
|
||||
}
|
||||
|
||||
func rules2Json(rules []SRbacRule) jsonutils.JSONObject {
|
||||
root := newRbacNode(levelService)
|
||||
for _, r := range rules {
|
||||
root.AddRule(r)
|
||||
}
|
||||
root.reduce()
|
||||
return root.json()
|
||||
}
|
||||
|
||||
func json2Rules(json jsonutils.JSONObject) ([]SRbacRule, error) {
|
||||
root := newRbacNode(levelService)
|
||||
err := root.parseJson(json)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "root.parseJson")
|
||||
}
|
||||
root.reduce()
|
||||
return root.GetRules(), nil
|
||||
}
|
||||
|
||||
func (n *sRbacNode) json() jsonutils.JSONObject {
|
||||
var result jsonutils.JSONObject
|
||||
if n.result != nil {
|
||||
return jsonutils.NewString(string(*n.result))
|
||||
} else {
|
||||
result = jsonutils.NewDict()
|
||||
if n.defNode != nil {
|
||||
result.(*jsonutils.JSONDict).Add(n.defNode.json(), "*")
|
||||
}
|
||||
for k := range n.downStream {
|
||||
result.(*jsonutils.JSONDict).Add(n.downStream[k].json(), k)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (n *sRbacNode) parseJson(input jsonutils.JSONObject) error {
|
||||
switch val := input.(type) {
|
||||
case *jsonutils.JSONString:
|
||||
ruleStr, err := val.GetString()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "val.GetString")
|
||||
}
|
||||
var result TRbacResult
|
||||
switch ruleStr {
|
||||
case string(Allow), string(AdminAllow), string(OwnerAllow), string(UserAllow), string(GuestAllow):
|
||||
result = Allow
|
||||
default:
|
||||
result = Deny
|
||||
}
|
||||
n.result = &result
|
||||
case *jsonutils.JSONDict:
|
||||
ruleJsonDict, err := val.GetMap()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "val.GetMap")
|
||||
}
|
||||
for key, ruleJson := range ruleJsonDict {
|
||||
if key == WILD_MATCH {
|
||||
n.defNode = newRbacNode(n.level + 1)
|
||||
err := n.defNode.parseJson(ruleJson)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "n.defNode.parseJson")
|
||||
}
|
||||
} else {
|
||||
n.downStream[key] = newRbacNode(n.level + 1)
|
||||
err := n.downStream[key].parseJson(ruleJson)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "n.downStream[key].parseJson")
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
return errors.Wrap(ErrUnsuportRuleData, input.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// 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 TestReduce(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
in []SRbacRule
|
||||
want []SRbacRule
|
||||
}{
|
||||
"merge1": {
|
||||
in: []SRbacRule{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "get",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "delete",
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
want: []SRbacRule{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
"merge2": {
|
||||
in: []SRbacRule{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "get",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "create",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "networks",
|
||||
Action: "get",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "networks",
|
||||
Action: "create",
|
||||
Result: Deny,
|
||||
},
|
||||
},
|
||||
want: []SRbacRule{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "networks",
|
||||
Action: "get",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "networks",
|
||||
Action: "create",
|
||||
Result: Deny,
|
||||
},
|
||||
},
|
||||
},
|
||||
"merge3": {
|
||||
in: []SRbacRule{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "get",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "get",
|
||||
Extra: []string{"vnc"},
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "create",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "update",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "delete",
|
||||
Result: Deny,
|
||||
},
|
||||
},
|
||||
want: []SRbacRule{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "get",
|
||||
Extra: []string{"vnc"},
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "delete",
|
||||
Result: Deny,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, c := range cases {
|
||||
got := reduceRules(c.in)
|
||||
t.Logf("[%s]: want: %s got: %s", name, c.want, got)
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,24 @@ func (ss SSortedStrings) ContainsAll(needles ...string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func Contains(a, b SSortedStrings) bool {
|
||||
_, _, bNoA := Split(a, b)
|
||||
if len(bNoA) == 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func Equals(a, b SSortedStrings) bool {
|
||||
aNoB, _, bNoA := Split(a, b)
|
||||
if len(aNoB) == 0 && len(bNoA) == 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func Split(a, b SSortedStrings) (aNoB SSortedStrings, aAndB SSortedStrings, bNoA SSortedStrings) {
|
||||
a_b := make([]string, 0)
|
||||
b_a := make([]string, 0)
|
||||
|
||||
Reference in New Issue
Block a user