mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
feature: policy group support
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbacutils
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
const (
|
||||
IP_PREFIX_SEP = ","
|
||||
)
|
||||
|
||||
func getPrefixes(prefstr string) []netutils.IPV4Prefix {
|
||||
if len(prefstr) == 0 {
|
||||
return nil
|
||||
}
|
||||
prefs := strings.Split(prefstr, IP_PREFIX_SEP)
|
||||
ret := make([]netutils.IPV4Prefix, 0)
|
||||
for _, pref := range prefs {
|
||||
p, err := netutils.NewIPV4Prefix(pref)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, p)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func MatchIPStrings(prefstr string, ipstr string) bool {
|
||||
prefs := getPrefixes(prefstr)
|
||||
return matchIP(prefs, ipstr)
|
||||
}
|
||||
|
||||
func matchIP(prefs []netutils.IPV4Prefix, ipstr string) bool {
|
||||
if len(prefs) == 0 {
|
||||
return true
|
||||
}
|
||||
ip, err := netutils.NewIPV4Addr(ipstr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, pref := range prefs {
|
||||
if pref.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbacutils
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMatchIPStrings(t *testing.T) {
|
||||
cases := []struct {
|
||||
prefixes string
|
||||
ip string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
prefixes: "",
|
||||
ip: "127.0.0.1",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
prefixes: "10.0.0.0/8",
|
||||
ip: "10.8.0.1",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
prefixes: "10.0.0.0/8,192.168.0.0/16",
|
||||
ip: "172.16.0.23",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
prefixes: "10.0.0.0/8,192.168.0.0/16",
|
||||
ip: "10.16.0.23",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
prefixes: "10.0.0.0/8,192.168.0.0/16",
|
||||
ip: "192.168.0.23",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := MatchIPStrings(c.prefixes, c.ip)
|
||||
if got != c.want {
|
||||
t.Errorf("prefix %s ip %s got %v want %v", c.prefixes, c.ip, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbacutils
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
type SRbacPolicy struct {
|
||||
// condition, when the policy takes effects
|
||||
// Deprecated
|
||||
Condition string
|
||||
|
||||
DomainId string
|
||||
|
||||
IsPublic bool
|
||||
|
||||
PublicScope TRbacScope
|
||||
|
||||
SharedDomainIds []string
|
||||
|
||||
Projects []string
|
||||
|
||||
Roles []string
|
||||
|
||||
Ips []netutils.IPV4Prefix
|
||||
|
||||
Auth bool // whether needs authentication
|
||||
|
||||
// scope, the scope of the policy, system/domain/project
|
||||
Scope TRbacScope
|
||||
// Deprecated
|
||||
// is_admin=true means scope=system, is_admin=false means scope=project
|
||||
IsAdmin bool
|
||||
|
||||
Rules TPolicy
|
||||
}
|
||||
|
||||
var (
|
||||
tenantEqualsPattern = regexp.MustCompile(`tenant\s*==\s*['"]?(\w+)['"]?`)
|
||||
roleContainsPattern = regexp.MustCompile(`roles.contains\(['"]?(\w+)['"]?\)`)
|
||||
)
|
||||
|
||||
func searchMatchStrings(pattern *regexp.Regexp, condstr string) []string {
|
||||
ret := make([]string, 0)
|
||||
matches := pattern.FindAllStringSubmatch(condstr, -1)
|
||||
for _, match := range matches {
|
||||
ret = append(ret, match[1])
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func searchMatchTenants(condstr string) []string {
|
||||
return searchMatchStrings(tenantEqualsPattern, condstr)
|
||||
}
|
||||
|
||||
func searchMatchRoles(condstr string) []string {
|
||||
return searchMatchStrings(roleContainsPattern, condstr)
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) Decode(policyJson jsonutils.JSONObject) error {
|
||||
policy.Condition, _ = policyJson.GetString("condition")
|
||||
if policyJson.Contains("projects") {
|
||||
projectJson, _ := policyJson.GetArray("projects")
|
||||
policy.Projects = jsonutils.JSONArray2StringArray(projectJson)
|
||||
}
|
||||
if policyJson.Contains("roles") {
|
||||
roleJson, _ := policyJson.GetArray("roles")
|
||||
policy.Roles = jsonutils.JSONArray2StringArray(roleJson)
|
||||
}
|
||||
|
||||
if len(policy.Projects) == 0 && len(policy.Roles) == 0 && len(policy.Condition) > 0 {
|
||||
// XXX hack
|
||||
// for smooth transtion from condition to projects&roles
|
||||
policy.Projects = searchMatchTenants(policy.Condition)
|
||||
policy.Roles = searchMatchRoles(policy.Condition)
|
||||
}
|
||||
// empty condition, no longer use this field
|
||||
policy.Condition = ""
|
||||
|
||||
if policyJson.Contains("ips") {
|
||||
ipsJson, _ := policyJson.GetArray("ips")
|
||||
ipStrs := jsonutils.JSONArray2StringArray(ipsJson)
|
||||
policy.Ips = make([]netutils.IPV4Prefix, 0)
|
||||
for _, ipStr := range ipStrs {
|
||||
if len(ipStr) == 0 || ipStr == "0.0.0.0" {
|
||||
continue
|
||||
}
|
||||
prefix, err := netutils.NewIPV4Prefix(ipStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
policy.Ips = append(policy.Ips, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
policy.Auth = jsonutils.QueryBoolean(policyJson, "auth", true)
|
||||
if len(policy.Ips) > 0 || len(policy.Roles) > 0 || len(policy.Projects) > 0 {
|
||||
policy.Auth = true
|
||||
}
|
||||
|
||||
scopeStr, _ := policyJson.GetString("scope")
|
||||
if len(scopeStr) > 0 {
|
||||
policy.Scope = TRbacScope(scopeStr)
|
||||
} else {
|
||||
policy.IsAdmin = jsonutils.QueryBoolean(policyJson, "is_admin", false)
|
||||
if len(policy.Scope) == 0 {
|
||||
if policy.IsAdmin {
|
||||
policy.Scope = ScopeSystem
|
||||
} else {
|
||||
policy.Scope = ScopeProject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
policyBody, err := policyJson.Get("policy")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Get policy")
|
||||
}
|
||||
policy.Rules, err = DecodePolicy(policyBody)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "DecodePolicy")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) Encode() jsonutils.JSONObject {
|
||||
ret := jsonutils.NewDict()
|
||||
|
||||
if !policy.Auth && len(policy.Projects) == 0 && len(policy.Roles) == 0 && len(policy.Ips) == 0 {
|
||||
ret.Add(jsonutils.JSONFalse, "auth")
|
||||
} else {
|
||||
ret.Add(jsonutils.JSONTrue, "auth")
|
||||
}
|
||||
|
||||
if len(policy.Projects) > 0 {
|
||||
ret.Add(jsonutils.NewStringArray(policy.Projects), "projects")
|
||||
}
|
||||
if len(policy.Roles) > 0 {
|
||||
ret.Add(jsonutils.NewStringArray(policy.Roles), "roles")
|
||||
}
|
||||
if len(policy.Ips) > 0 {
|
||||
ipStrs := make([]string, len(policy.Ips))
|
||||
for i := range policy.Ips {
|
||||
ipStrs[i] = policy.Ips[i].String()
|
||||
}
|
||||
ret.Add(jsonutils.NewStringArray(ipStrs), "ips")
|
||||
}
|
||||
|
||||
ret.Add(jsonutils.NewString(string(policy.Scope)), "scope")
|
||||
|
||||
ret.Add(policy.Rules.Encode(), "policy")
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) IsSystemWidePolicy() bool {
|
||||
return (len(policy.DomainId) == 0 || (policy.IsPublic && policy.PublicScope == ScopeSystem)) && len(policy.Roles) == 0 && len(policy.Projects) == 0
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchDomain(domainId string) bool {
|
||||
if len(policy.DomainId) == 0 || len(domainId) == 0 {
|
||||
return true
|
||||
}
|
||||
if policy.DomainId == domainId {
|
||||
return true
|
||||
}
|
||||
if policy.IsPublic {
|
||||
if policy.PublicScope == ScopeSystem {
|
||||
return true
|
||||
}
|
||||
if contains(policy.SharedDomainIds, domainId) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchProject(projectName string) bool {
|
||||
if len(policy.Projects) == 0 || len(projectName) == 0 {
|
||||
return true
|
||||
}
|
||||
if contains(policy.Projects, projectName) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchRoles(roleNames []string) bool {
|
||||
if len(policy.Roles) == 0 {
|
||||
return true
|
||||
}
|
||||
if intersect(policy.Roles, roleNames) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// check whether policy maches a userCred
|
||||
// return value
|
||||
// bool isMatched
|
||||
// int match weight, the higher the value, the more exact the match
|
||||
// the more exact match wins
|
||||
func (policy *SRbacPolicy) Match(userCred IRbacIdentity2) (bool, int) {
|
||||
if !policy.Auth && len(policy.Roles) == 0 && len(policy.Projects) == 0 && len(policy.Ips) == 0 {
|
||||
return true, 1
|
||||
}
|
||||
if userCred == nil || len(userCred.GetTokenString()) == 0 {
|
||||
return false, 0
|
||||
}
|
||||
weight := 0
|
||||
if policy.MatchDomain(userCred.GetProjectDomainId()) {
|
||||
if len(policy.DomainId) > 0 {
|
||||
if policy.DomainId == userCred.GetProjectDomainId() {
|
||||
weight += 30 // exact domain match
|
||||
} else if len(policy.SharedDomainIds) > 0 {
|
||||
weight += 20 // shared domain match
|
||||
} else {
|
||||
weight += 10 // else, system scope match
|
||||
}
|
||||
}
|
||||
if policy.MatchRoles(userCred.GetRoles()) {
|
||||
if len(policy.Roles) != 0 {
|
||||
weight += 100
|
||||
}
|
||||
if policy.MatchProject(userCred.GetProjectName()) {
|
||||
if len(policy.Projects) > 0 {
|
||||
weight += 1000
|
||||
}
|
||||
if len(policy.Ips) == 0 || containsIp(policy.Ips, userCred.GetLoginIp()) {
|
||||
if len(policy.Ips) > 0 {
|
||||
weight += 10000
|
||||
}
|
||||
return true, weight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbacutils
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
)
|
||||
|
||||
type TPolicy []SRbacRule
|
||||
|
||||
func (policy TPolicy) getMatchRule(req []string) *SRbacRule {
|
||||
service := WILD_MATCH
|
||||
if len(req) > levelService {
|
||||
service = req[levelService]
|
||||
}
|
||||
resource := WILD_MATCH
|
||||
if len(req) > levelResource {
|
||||
resource = req[levelResource]
|
||||
}
|
||||
action := WILD_MATCH
|
||||
if len(req) > levelAction {
|
||||
action = req[levelAction]
|
||||
}
|
||||
var extra []string
|
||||
if len(req) > levelExtra {
|
||||
extra = req[levelExtra:]
|
||||
} else {
|
||||
extra = make([]string, 0)
|
||||
}
|
||||
|
||||
return policy.GetMatchRule(service, resource, action, extra...)
|
||||
}
|
||||
|
||||
func (policy TPolicy) GetMatchRule(service string, resource string, action string, extra ...string) *SRbacRule {
|
||||
return GetMatchRule(policy, service, resource, action, extra...)
|
||||
}
|
||||
|
||||
func DecodePolicy(policyJson jsonutils.JSONObject) (TPolicy, error) {
|
||||
rules, err := json2Rules(policyJson)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "json2Rules")
|
||||
}
|
||||
if len(rules) == 0 {
|
||||
return nil, ErrEmptyPolicy
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func DecodePolicyData(input jsonutils.JSONObject) (TPolicy, error) {
|
||||
policyData, err := input.Get("policy")
|
||||
if err != nil || policyData == nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidFormat, "invalid policy data")
|
||||
}
|
||||
return DecodePolicy(policyData)
|
||||
}
|
||||
|
||||
func (policy TPolicy) Encode() jsonutils.JSONObject {
|
||||
return rules2Json(policy)
|
||||
}
|
||||
|
||||
func (policy TPolicy) EncodeData() jsonutils.JSONObject {
|
||||
ret := jsonutils.NewDict()
|
||||
ret.Add(policy.Encode(), "policy")
|
||||
return ret
|
||||
}
|
||||
|
||||
func (policy TPolicy) Explain(request [][]string) [][]string {
|
||||
output := make([][]string, len(request))
|
||||
for i := 0; i < len(request); i += 1 {
|
||||
rule := policy.getMatchRule(request[i])
|
||||
if rule == nil {
|
||||
output[i] = append(request[i], string(Deny))
|
||||
} else {
|
||||
output[i] = append(request[i], string(rule.Result))
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbacutils
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
)
|
||||
|
||||
/*
|
||||
type SPolicyInfo struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
DomainId string `json:"domain_id"`
|
||||
IsPublic bool `json:"is_public"`
|
||||
PublicScope string `json:"public_scope"`
|
||||
SharedDomainIds []string `json:"shared_domain_ids"`
|
||||
Scope TRbacScope `json:"scope"`
|
||||
Policy *SRbacPolicyCore `json:"policy"`
|
||||
}
|
||||
|
||||
func GetMatchedPolicies(policies []SPolicyInfo, userCred IRbacIdentity) (TPolicySet, []string) {
|
||||
matchedPolicies := make([]*SRbacPolicyCore, 0)
|
||||
matchedNames := make([]string, 0)
|
||||
for i := range policies {
|
||||
isMatched, _ := policies[i].Policy.Match(userCred)
|
||||
if !isMatched {
|
||||
continue
|
||||
}
|
||||
matchedPolicies = append(matchedPolicies, policies[i].Policy)
|
||||
matchedNames = append(matchedNames, policies[i].Name)
|
||||
}
|
||||
return matchedPolicies, matchedNames
|
||||
}*/
|
||||
|
||||
type TPolicyGroup map[TRbacScope]TPolicySet
|
||||
|
||||
func DecodePolicyGroup(json jsonutils.JSONObject) (TPolicyGroup, error) {
|
||||
jmap, err := json.GetMap()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidFormat, "invalid json: not a map")
|
||||
}
|
||||
group := TPolicyGroup{}
|
||||
for k := range jmap {
|
||||
group[TRbacScope(k)], err = DecodePolicySet(jmap[k])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "decode %s", k)
|
||||
}
|
||||
}
|
||||
return group, nil
|
||||
}
|
||||
|
||||
func (sets TPolicyGroup) HighestScope() TRbacScope {
|
||||
for _, s := range []TRbacScope{
|
||||
ScopeSystem,
|
||||
ScopeDomain,
|
||||
ScopeProject,
|
||||
ScopeUser,
|
||||
} {
|
||||
if _, ok := sets[s]; ok {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return ScopeNone
|
||||
}
|
||||
|
||||
func (sets TPolicyGroup) Encode() jsonutils.JSONObject {
|
||||
j := jsonutils.NewDict()
|
||||
for k := range sets {
|
||||
j.Set(string(k), sets[k].Encode())
|
||||
}
|
||||
return j
|
||||
}
|
||||
@@ -14,27 +14,14 @@
|
||||
|
||||
package rbacutils
|
||||
|
||||
type SPolicyInfo struct {
|
||||
Id string
|
||||
Name string
|
||||
Policy *SRbacPolicy
|
||||
}
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
type TPolicySet []*SRbacPolicy
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
)
|
||||
|
||||
func GetMatchedPolicies(policies []SPolicyInfo, userCred IRbacIdentity) (TPolicySet, []string) {
|
||||
matchedPolicies := make([]*SRbacPolicy, 0)
|
||||
matchedNames := make([]string, 0)
|
||||
for i := range policies {
|
||||
isMatched, _ := policies[i].Policy.Match(userCred)
|
||||
if !isMatched {
|
||||
continue
|
||||
}
|
||||
matchedPolicies = append(matchedPolicies, policies[i].Policy)
|
||||
matchedNames = append(matchedNames, policies[i].Name)
|
||||
}
|
||||
return matchedPolicies, matchedNames
|
||||
}
|
||||
type TPolicySet []TPolicy
|
||||
|
||||
func (policies TPolicySet) GetMatchRules(service string, resource string, action string, extra ...string) []SRbacRule {
|
||||
matchRules := make([]SRbacRule, 0)
|
||||
@@ -47,6 +34,30 @@ func (policies TPolicySet) GetMatchRules(service string, resource string, action
|
||||
return matchRules
|
||||
}
|
||||
|
||||
func DecodePolicySet(jsonObj jsonutils.JSONObject) (TPolicySet, error) {
|
||||
jsonArr, err := jsonObj.GetArray()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidFormat, "invalid json: not an array")
|
||||
}
|
||||
set := TPolicySet{}
|
||||
for i := range jsonArr {
|
||||
policy, err := DecodePolicy(jsonArr[i])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "decode %d", i)
|
||||
}
|
||||
set = append(set, policy)
|
||||
}
|
||||
return set, nil
|
||||
}
|
||||
|
||||
func (policies TPolicySet) Encode() jsonutils.JSONObject {
|
||||
obj := make([]jsonutils.JSONObject, len(policies))
|
||||
for i := range policies {
|
||||
obj[i] = policies[i].Encode()
|
||||
}
|
||||
return jsonutils.NewArray(obj...)
|
||||
}
|
||||
|
||||
// ViolatedBy: policies中deny的权限,但是assign中却是allow
|
||||
// if any assign allow, but policies deny
|
||||
// OR
|
||||
@@ -70,9 +81,9 @@ func (policies TPolicySet) violatedBySet(assign TPolicySet, expect TRbacResult)
|
||||
return false
|
||||
}
|
||||
|
||||
func (policies TPolicySet) violatedByPolicy(policy *SRbacPolicy, expect TRbacResult) bool {
|
||||
for i := range policy.Rules {
|
||||
rule := policy.Rules[i]
|
||||
func (policies TPolicySet) violatedByPolicy(policy TPolicy, expect TRbacResult) bool {
|
||||
for i := range policy {
|
||||
rule := policy[i]
|
||||
if rule.Result != expect {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -26,32 +26,28 @@ func TestTPolicySet_Violate(t *testing.T) {
|
||||
{
|
||||
name: "case1",
|
||||
p1: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
p2: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -60,40 +56,34 @@ func TestTPolicySet_Violate(t *testing.T) {
|
||||
{
|
||||
name: "case2",
|
||||
p1: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: "comptue",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: "comptue",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
p2: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -102,60 +92,52 @@ func TestTPolicySet_Violate(t *testing.T) {
|
||||
{
|
||||
name: "case3",
|
||||
p1: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "create",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "create",
|
||||
Result: Deny,
|
||||
},
|
||||
},
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: "comptue",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: "comptue",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
p2: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Deny,
|
||||
},
|
||||
},
|
||||
&SRbacPolicy{
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: "comptue",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "get",
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: "comptue",
|
||||
Resource: "servers",
|
||||
Action: WILD_MATCH,
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "get",
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -164,30 +146,24 @@ func TestTPolicySet_Violate(t *testing.T) {
|
||||
{
|
||||
name: "case4",
|
||||
p2: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Scope: ScopeDomain,
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
p1: TPolicySet{
|
||||
&SRbacPolicy{
|
||||
Scope: ScopeDomain,
|
||||
Rules: []SRbacRule{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
{
|
||||
{
|
||||
Service: WILD_MATCH,
|
||||
Result: Allow,
|
||||
},
|
||||
{
|
||||
Service: "compute",
|
||||
Resource: "servers",
|
||||
Action: "list",
|
||||
Result: Deny,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+45
-407
@@ -15,12 +15,9 @@
|
||||
package rbacutils
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
@@ -87,32 +84,6 @@ func (s1 TRbacScope) HigherThan(s2 TRbacScope) bool {
|
||||
return scopeScore[s1] > scopeScore[s2]
|
||||
}
|
||||
|
||||
type SRbacPolicy struct {
|
||||
// condition, when the policy takes effects
|
||||
// Deprecated
|
||||
Condition string
|
||||
|
||||
DomainId string
|
||||
|
||||
IsPublic bool
|
||||
PublicScope TRbacScope
|
||||
SharedDomainIds []string
|
||||
|
||||
Projects []string
|
||||
Roles []string
|
||||
Ips []netutils.IPV4Prefix
|
||||
Auth bool // whether needs authentication
|
||||
|
||||
// scope, the scope of the policy, system/domain/project
|
||||
Scope TRbacScope
|
||||
// Deprecated
|
||||
// is_admin=true means scope=system, is_admin=false means scope=project
|
||||
IsAdmin bool
|
||||
|
||||
// rules, the exact rules
|
||||
Rules []SRbacRule
|
||||
}
|
||||
|
||||
type SRbacRule struct {
|
||||
Service string
|
||||
Resource string
|
||||
@@ -201,33 +172,6 @@ func (rule *SRbacRule) match(service string, resource string, action string, ext
|
||||
return true, matched, weight
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) getMatchRule(req []string) *SRbacRule {
|
||||
service := WILD_MATCH
|
||||
if len(req) > levelService {
|
||||
service = req[levelService]
|
||||
}
|
||||
resource := WILD_MATCH
|
||||
if len(req) > levelResource {
|
||||
resource = req[levelResource]
|
||||
}
|
||||
action := WILD_MATCH
|
||||
if len(req) > levelAction {
|
||||
action = req[levelAction]
|
||||
}
|
||||
var extra []string
|
||||
if len(req) > levelExtra {
|
||||
extra = req[levelExtra:]
|
||||
} else {
|
||||
extra = make([]string, 0)
|
||||
}
|
||||
|
||||
return policy.GetMatchRule(service, resource, action, extra...)
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) GetMatchRule(service string, resource string, action string, extra ...string) *SRbacRule {
|
||||
return GetMatchRule(policy.Rules, service, resource, action, extra...)
|
||||
}
|
||||
|
||||
var (
|
||||
ShowMatchRuleDebug = false
|
||||
)
|
||||
@@ -252,103 +196,6 @@ func GetMatchRule(rules []SRbacRule, service string, resource string, action str
|
||||
return matchRule
|
||||
}
|
||||
|
||||
var (
|
||||
tenantEqualsPattern = regexp.MustCompile(`tenant\s*==\s*['"]?(\w+)['"]?`)
|
||||
roleContainsPattern = regexp.MustCompile(`roles.contains\(['"]?(\w+)['"]?\)`)
|
||||
)
|
||||
|
||||
func searchMatchStrings(pattern *regexp.Regexp, condstr string) []string {
|
||||
ret := make([]string, 0)
|
||||
matches := pattern.FindAllStringSubmatch(condstr, -1)
|
||||
for _, match := range matches {
|
||||
ret = append(ret, match[1])
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func searchMatchTenants(condstr string) []string {
|
||||
return searchMatchStrings(tenantEqualsPattern, condstr)
|
||||
}
|
||||
|
||||
func searchMatchRoles(condstr string) []string {
|
||||
return searchMatchStrings(roleContainsPattern, condstr)
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) Decode(policyJson jsonutils.JSONObject) error {
|
||||
policy.Condition, _ = policyJson.GetString("condition")
|
||||
if policyJson.Contains("projects") {
|
||||
projectJson, _ := policyJson.GetArray("projects")
|
||||
policy.Projects = jsonutils.JSONArray2StringArray(projectJson)
|
||||
}
|
||||
if policyJson.Contains("roles") {
|
||||
roleJson, _ := policyJson.GetArray("roles")
|
||||
policy.Roles = jsonutils.JSONArray2StringArray(roleJson)
|
||||
}
|
||||
|
||||
if len(policy.Projects) == 0 && len(policy.Roles) == 0 && len(policy.Condition) > 0 {
|
||||
// XXX hack
|
||||
// for smooth transtion from condition to projects&roles
|
||||
policy.Projects = searchMatchTenants(policy.Condition)
|
||||
policy.Roles = searchMatchRoles(policy.Condition)
|
||||
}
|
||||
// empty condition, no longer use this field
|
||||
policy.Condition = ""
|
||||
|
||||
scopeStr, _ := policyJson.GetString("scope")
|
||||
if len(scopeStr) > 0 {
|
||||
policy.Scope = TRbacScope(scopeStr)
|
||||
} else {
|
||||
policy.IsAdmin = jsonutils.QueryBoolean(policyJson, "is_admin", false)
|
||||
if len(policy.Scope) == 0 {
|
||||
if policy.IsAdmin {
|
||||
policy.Scope = ScopeSystem
|
||||
} else {
|
||||
policy.Scope = ScopeProject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if policyJson.Contains("ips") {
|
||||
ipsJson, _ := policyJson.GetArray("ips")
|
||||
ipStrs := jsonutils.JSONArray2StringArray(ipsJson)
|
||||
policy.Ips = make([]netutils.IPV4Prefix, 0)
|
||||
for _, ipStr := range ipStrs {
|
||||
if len(ipStr) == 0 || ipStr == "0.0.0.0" {
|
||||
continue
|
||||
}
|
||||
prefix, err := netutils.NewIPV4Prefix(ipStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
policy.Ips = append(policy.Ips, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
policy.Auth = jsonutils.QueryBoolean(policyJson, "auth", true)
|
||||
if len(policy.Ips) > 0 || len(policy.Roles) > 0 || len(policy.Projects) > 0 {
|
||||
policy.Auth = true
|
||||
}
|
||||
|
||||
ruleJson, err := policyJson.Get("policy")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
/*rules, err := decode(ruleJson, SRbacRule{}, levelService)*/
|
||||
rules, err := json2Rules(ruleJson)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "json2Rules")
|
||||
}
|
||||
|
||||
if len(rules) == 0 {
|
||||
return ErrEmptyPolicy
|
||||
}
|
||||
|
||||
policy.Rules = rules
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
levelService = 0
|
||||
levelResource = 1
|
||||
@@ -356,55 +203,6 @@ const (
|
||||
levelExtra = 3
|
||||
)
|
||||
|
||||
func decode(rules jsonutils.JSONObject, decodeRule SRbacRule, level int) ([]SRbacRule, error) {
|
||||
switch rules.(type) {
|
||||
case *jsonutils.JSONString:
|
||||
ruleJsonStr := rules.(*jsonutils.JSONString)
|
||||
ruleStr, _ := ruleJsonStr.GetString()
|
||||
switch ruleStr {
|
||||
case string(Allow), string(AdminAllow), string(OwnerAllow), string(UserAllow), string(GuestAllow):
|
||||
decodeRule.Result = Allow
|
||||
default:
|
||||
decodeRule.Result = Deny
|
||||
// default:
|
||||
// return nil, fmt.Errorf("unsupported rule string %s", ruleStr)
|
||||
}
|
||||
return []SRbacRule{decodeRule}, nil
|
||||
case *jsonutils.JSONDict:
|
||||
ruleJsonDict, err := rules.GetMap()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get rule map fail")
|
||||
}
|
||||
rules := make([]SRbacRule, 0)
|
||||
for key, ruleJson := range ruleJsonDict {
|
||||
rule := decodeRule
|
||||
switch {
|
||||
case level == levelService:
|
||||
rule.Service = key
|
||||
case level == levelResource:
|
||||
rule.Resource = key
|
||||
case level == levelAction:
|
||||
rule.Action = key
|
||||
case level >= levelExtra:
|
||||
if rule.Extra == nil {
|
||||
rule.Extra = make([]string, 1)
|
||||
rule.Extra[0] = key
|
||||
} else {
|
||||
rule.Extra = append(rule.Extra, key)
|
||||
}
|
||||
}
|
||||
decoded, err := decode(ruleJson, rule, level+1)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode")
|
||||
}
|
||||
rules = append(rules, decoded...)
|
||||
}
|
||||
return rules, nil
|
||||
default:
|
||||
return nil, errors.Wrap(ErrUnsuportRuleData, rules.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (rule *SRbacRule) toStringArray() []string {
|
||||
strArr := make([]string, 0)
|
||||
strArr = append(strArr, rule.Service)
|
||||
@@ -420,106 +218,6 @@ func (rule *SRbacRule) toStringArray() []string {
|
||||
return strArr[0 : i+1]
|
||||
}
|
||||
|
||||
func addRule2Json(nodeJson *jsonutils.JSONDict, keys []string, result TRbacResult) error {
|
||||
if len(keys) == 1 {
|
||||
if nodeJson.Contains(keys[0]) {
|
||||
nextJson, _ := nodeJson.Get(keys[0])
|
||||
switch nextJson.(type) {
|
||||
case *jsonutils.JSONString: // conflict??
|
||||
return ErrConflict // fmt.Errorf("conflict?")
|
||||
case *jsonutils.JSONDict:
|
||||
nextJsonDict := nextJson.(*jsonutils.JSONDict)
|
||||
addRule2Json(nextJsonDict, []string{WILD_MATCH}, result)
|
||||
return nil
|
||||
default:
|
||||
return ErrInvalidRules // fmt.Errorf("invalid rules")
|
||||
}
|
||||
} else {
|
||||
nodeJson.Add(jsonutils.NewString(string(result)), keys[0])
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// len(keys) > 1
|
||||
exist, _ := nodeJson.Get(keys[0])
|
||||
if exist != nil {
|
||||
switch exist.(type) {
|
||||
case *jsonutils.JSONString: // need restruct
|
||||
newDict := jsonutils.NewDict()
|
||||
newDict.Add(exist, "*")
|
||||
nodeJson.Set(keys[0], newDict)
|
||||
return addRule2Json(newDict, keys[1:], result)
|
||||
case *jsonutils.JSONDict:
|
||||
existDict := exist.(*jsonutils.JSONDict)
|
||||
return addRule2Json(existDict, keys[1:], result)
|
||||
default:
|
||||
return ErrInvalidRules // fmt.Errorf("invalid rules")
|
||||
}
|
||||
} else {
|
||||
next := jsonutils.NewDict()
|
||||
nodeJson.Add(next, keys[0])
|
||||
return addRule2Json(next, keys[1:], result)
|
||||
}
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) Encode() (jsonutils.JSONObject, error) {
|
||||
/*rules := jsonutils.NewDict()
|
||||
for i := 0; i < len(policy.Rules); i += 1 {
|
||||
keys := policy.Rules[i].toStringArray()
|
||||
err := addRule2Json(rules, keys, policy.Rules[i].Result)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "addRule2Json")
|
||||
}
|
||||
}*/
|
||||
|
||||
rules := rules2Json(policy.Rules)
|
||||
|
||||
ret := jsonutils.NewDict()
|
||||
// ret.Add(jsonutils.NewString(policy.Condition), "condition")
|
||||
// if policy.IsAdmin {
|
||||
// ret.Add(jsonutils.JSONTrue, "is_admin")
|
||||
// } else {
|
||||
// ret.Add(jsonutils.JSONFalse, "is_admin")
|
||||
// }
|
||||
|
||||
ret.Add(jsonutils.NewString(string(policy.Scope)), "scope")
|
||||
|
||||
if !policy.Auth && len(policy.Projects) == 0 && len(policy.Roles) == 0 && len(policy.Ips) == 0 {
|
||||
ret.Add(jsonutils.JSONFalse, "auth")
|
||||
} else {
|
||||
ret.Add(jsonutils.JSONTrue, "auth")
|
||||
}
|
||||
|
||||
if len(policy.Projects) > 0 {
|
||||
ret.Add(jsonutils.NewStringArray(policy.Projects), "projects")
|
||||
}
|
||||
if len(policy.Roles) > 0 {
|
||||
ret.Add(jsonutils.NewStringArray(policy.Roles), "roles")
|
||||
}
|
||||
if len(policy.Ips) > 0 {
|
||||
ipStrs := make([]string, len(policy.Ips))
|
||||
for i := range policy.Ips {
|
||||
ipStrs[i] = policy.Ips[i].String()
|
||||
}
|
||||
ret.Add(jsonutils.NewStringArray(ipStrs), "ips")
|
||||
}
|
||||
|
||||
ret.Add(rules, "policy")
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) Explain(request [][]string) [][]string {
|
||||
output := make([][]string, len(request))
|
||||
for i := 0; i < len(request); i += 1 {
|
||||
rule := policy.getMatchRule(request[i])
|
||||
if rule == nil {
|
||||
output[i] = append(request[i], string(Deny))
|
||||
} else {
|
||||
output[i] = append(request[i], string(rule.Result))
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func contains(s1 []string, s string) bool {
|
||||
for i := range s1 {
|
||||
if s1[i] == s {
|
||||
@@ -558,7 +256,18 @@ func containsIp(ips []netutils.IPV4Prefix, ipStr string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
FAKE_TOKEN = "fake_token"
|
||||
)
|
||||
|
||||
type IRbacIdentity interface {
|
||||
GetProjectId() string
|
||||
GetRoleIds() []string
|
||||
GetLoginIp() string
|
||||
GetTokenString() string
|
||||
}
|
||||
|
||||
type IRbacIdentity2 interface {
|
||||
GetProjectDomainId() string
|
||||
GetProjectName() string
|
||||
GetRoles() []string
|
||||
@@ -566,128 +275,57 @@ type IRbacIdentity interface {
|
||||
GetTokenString() string
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) IsSystemWidePolicy() bool {
|
||||
return (len(policy.DomainId) == 0 || (policy.IsPublic && policy.PublicScope == ScopeSystem)) && len(policy.Roles) == 0 && len(policy.Projects) == 0
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchDomain(domainId string) bool {
|
||||
if len(policy.DomainId) == 0 || len(domainId) == 0 {
|
||||
return true
|
||||
}
|
||||
if policy.DomainId == domainId {
|
||||
return true
|
||||
}
|
||||
if policy.IsPublic {
|
||||
if policy.PublicScope == ScopeSystem {
|
||||
return true
|
||||
}
|
||||
if contains(policy.SharedDomainIds, domainId) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchProject(projectName string) bool {
|
||||
if len(policy.Projects) == 0 || len(projectName) == 0 {
|
||||
return true
|
||||
}
|
||||
if contains(policy.Projects, projectName) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (policy *SRbacPolicy) MatchRoles(roleNames []string) bool {
|
||||
if len(policy.Roles) == 0 {
|
||||
return true
|
||||
}
|
||||
if intersect(policy.Roles, roleNames) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// check whether policy maches a userCred
|
||||
// return value
|
||||
// bool isMatched
|
||||
// int match weight, the higher the value, the more exact the match
|
||||
// the more exact match wins
|
||||
func (policy *SRbacPolicy) Match(userCred IRbacIdentity) (bool, int) {
|
||||
if !policy.Auth && len(policy.Roles) == 0 && len(policy.Projects) == 0 && len(policy.Ips) == 0 {
|
||||
return true, 1
|
||||
}
|
||||
if userCred == nil || len(userCred.GetTokenString()) == 0 {
|
||||
return false, 0
|
||||
}
|
||||
weight := 0
|
||||
if policy.MatchDomain(userCred.GetProjectDomainId()) {
|
||||
if len(policy.DomainId) > 0 {
|
||||
if policy.DomainId == userCred.GetProjectDomainId() {
|
||||
weight += 30 // exact domain match
|
||||
} else if len(policy.SharedDomainIds) > 0 {
|
||||
weight += 20 // shared domain match
|
||||
} else {
|
||||
weight += 10 // else, system scope match
|
||||
}
|
||||
}
|
||||
if policy.MatchRoles(userCred.GetRoles()) {
|
||||
if len(policy.Roles) != 0 {
|
||||
weight += 100
|
||||
}
|
||||
if policy.MatchProject(userCred.GetProjectName()) {
|
||||
if len(policy.Projects) > 0 {
|
||||
weight += 1000
|
||||
}
|
||||
if len(policy.Ips) == 0 || containsIp(policy.Ips, userCred.GetLoginIp()) {
|
||||
if len(policy.Ips) > 0 {
|
||||
weight += 10000
|
||||
}
|
||||
return true, weight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
|
||||
type sSimpleRbacIdentity struct {
|
||||
domainId string
|
||||
projectName string
|
||||
roleNames []string
|
||||
loginIp string
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetProjectDomainId() string {
|
||||
return id.domainId
|
||||
projectDomainId string
|
||||
projectId string
|
||||
projectName string
|
||||
roleIds []string
|
||||
roles []string
|
||||
ip string
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetRoles() []string {
|
||||
return id.roleNames
|
||||
return id.roles
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetRoleIds() []string {
|
||||
return id.roleIds
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetProjectName() string {
|
||||
return id.projectName
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetProjectId() string {
|
||||
return id.projectId
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetProjectDomainId() string {
|
||||
return id.projectDomainId
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetLoginIp() string {
|
||||
return id.loginIp
|
||||
return id.ip
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetTokenString() string {
|
||||
return "faketoken"
|
||||
return FAKE_TOKEN
|
||||
}
|
||||
|
||||
func NewRbacIdentity(domainId, projectName string, roleNames []string) IRbacIdentity {
|
||||
return NewRbacIdentity2(domainId, projectName, roleNames, "")
|
||||
}
|
||||
|
||||
func NewRbacIdentity2(domainId, projectName string, roleNames []string, loginIp string) IRbacIdentity {
|
||||
func newRbacIdentity2(projectDomainId, projectName string, roles []string, ip string) IRbacIdentity2 {
|
||||
return sSimpleRbacIdentity{
|
||||
domainId: domainId,
|
||||
projectName: projectName,
|
||||
roleNames: roleNames,
|
||||
loginIp: loginIp,
|
||||
projectDomainId: projectDomainId,
|
||||
projectName: projectName,
|
||||
roles: roles,
|
||||
ip: ip,
|
||||
}
|
||||
}
|
||||
|
||||
func NewRbacIdentity(projectId string, roleIds []string, ip string) IRbacIdentity {
|
||||
return sSimpleRbacIdentity{
|
||||
projectId: projectId,
|
||||
roleIds: roleIds,
|
||||
ip: ip,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -203,11 +203,7 @@ func TestSRabcPolicy_Encode(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
policyJson1, err := policy.Encode()
|
||||
if err != nil {
|
||||
t.Errorf("encode error %s", err)
|
||||
return
|
||||
}
|
||||
policyJson1 := policy.Encode()
|
||||
|
||||
policy2 := SRbacPolicy{}
|
||||
|
||||
@@ -217,11 +213,7 @@ func TestSRabcPolicy_Encode(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
policyJson2, err := policy2.Encode()
|
||||
if err != nil {
|
||||
t.Errorf("encode error 2 %s", err)
|
||||
return
|
||||
}
|
||||
policyJson2 := policy2.Encode()
|
||||
|
||||
policyStr1 := policyJson1.PrettyString()
|
||||
policyStr2 := policyJson2.PrettyString()
|
||||
@@ -235,6 +227,7 @@ func TestSRabcPolicy_Encode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestSRabcPolicy_Explain(t *testing.T) {
|
||||
policyStr := `{
|
||||
"condition": "usercred.project != \"system\" && usercred.roles==\"projectowner\"",
|
||||
@@ -282,6 +275,7 @@ func TestSRabcPolicy_Explain(t *testing.T) {
|
||||
|
||||
t.Logf("%#v", output)
|
||||
}
|
||||
*/
|
||||
|
||||
func TestConditionParser(t *testing.T) {
|
||||
condition := `tenant=="system" && roles.contains("admin")`
|
||||
@@ -291,44 +285,16 @@ func TestConditionParser(t *testing.T) {
|
||||
t.Logf("%s", roles)
|
||||
}
|
||||
|
||||
type sRbacIdentity struct {
|
||||
DomainId string
|
||||
Project string
|
||||
Roles []string
|
||||
Ip string
|
||||
Token string
|
||||
}
|
||||
|
||||
func (ri *sRbacIdentity) GetProjectDomainId() string {
|
||||
return ri.DomainId
|
||||
}
|
||||
|
||||
func (ri *sRbacIdentity) GetProjectName() string {
|
||||
return ri.Project
|
||||
}
|
||||
|
||||
func (ri *sRbacIdentity) GetRoles() []string {
|
||||
return ri.Roles
|
||||
}
|
||||
|
||||
func (ri *sRbacIdentity) GetLoginIp() string {
|
||||
return ri.Ip
|
||||
}
|
||||
|
||||
func (ri *sRbacIdentity) GetTokenString() string {
|
||||
return ri.Token
|
||||
}
|
||||
|
||||
func TestSRbacPolicyMatch(t *testing.T) {
|
||||
prefix, _ := netutils.NewIPV4Prefix("10.168.22.0/24")
|
||||
cases := []struct {
|
||||
policy SRbacPolicy
|
||||
userCred IRbacIdentity
|
||||
userCred IRbacIdentity2
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
SRbacPolicy{},
|
||||
&sRbacIdentity{},
|
||||
newRbacIdentity2("", "", nil, ""),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -340,20 +306,14 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
SRbacPolicy{
|
||||
Projects: []string{"system"},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", nil, ""),
|
||||
true,
|
||||
},
|
||||
{
|
||||
SRbacPolicy{
|
||||
Projects: []string{"system"},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "demo",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "demo", nil, ""),
|
||||
false,
|
||||
},
|
||||
{
|
||||
@@ -361,11 +321,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Projects: []string{"system"},
|
||||
Roles: []string{"admin"},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"admin"},
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"admin"}, ""),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -373,11 +329,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Projects: []string{"system"},
|
||||
Roles: []string{"admin"},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"admin", "_member_"},
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"admin", "_member_"}, ""),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -385,11 +337,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Projects: []string{"system"},
|
||||
Roles: []string{"admin"},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"_member_"},
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"_member_"}, ""),
|
||||
false,
|
||||
},
|
||||
{
|
||||
@@ -413,12 +361,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"admin"},
|
||||
Ips: []netutils.IPV4Prefix{prefix},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"admin"},
|
||||
Ip: "10.0.0.23",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"admin"}, "10.0.0.23"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
@@ -427,12 +370,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"admin"},
|
||||
Ips: []netutils.IPV4Prefix{prefix},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"admin"},
|
||||
Ip: "10.168.22.23",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"admin"}, "10.168.22.23"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -441,12 +379,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"admin"},
|
||||
Ips: []netutils.IPV4Prefix{prefix},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"_member_"},
|
||||
Ip: "10.168.22.23",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"_member_"}, "10.168.22.23"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
@@ -454,12 +387,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"admin"},
|
||||
Ips: []netutils.IPV4Prefix{prefix},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"_member_", "admin"},
|
||||
Ip: "10.168.22.23",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"_member_", "admin"}, "10.168.22.23"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -468,12 +396,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"admin", "_member_"},
|
||||
Ips: []netutils.IPV4Prefix{prefix},
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "system",
|
||||
Roles: []string{"_member_", "projectowner"},
|
||||
Ip: "10.168.22.23",
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "system", []string{"_member_", "projectowner"}, "10.168.22.23"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -482,11 +405,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"domain_admin"},
|
||||
Auth: true,
|
||||
},
|
||||
&sRbacIdentity{
|
||||
Project: "ldapproj",
|
||||
Roles: []string{"domain_admin"},
|
||||
Token: "faketoken",
|
||||
},
|
||||
newRbacIdentity2("", "ldapproj", []string{"domain_admin"}, ""),
|
||||
true,
|
||||
},
|
||||
{
|
||||
@@ -495,7 +414,7 @@ func TestSRbacPolicyMatch(t *testing.T) {
|
||||
Roles: []string{"admin"},
|
||||
Auth: true,
|
||||
},
|
||||
NewRbacIdentity("", "", []string{"admin"}),
|
||||
newRbacIdentity2("", "", []string{"admin"}, ""),
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user