// 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" "yunion.io/x/jsonutils" "yunion.io/x/pkg/util/netutils" ) func TestSRabcRule_Match(t *testing.T) { all := SRbacRule{Service: "*", Resource: "*", Action: "*"} compute := SRbacRule{Service: "compute", Resource: "*", Action: "*"} getOnly := SRbacRule{Service: "*", Resource: "*", Action: "get"} listOnly := SRbacRule{Service: "*", Resource: "*", Action: "list"} serverList := SRbacRule{Service: "compute", Resource: "server", Action: "list"} serverPerform := SRbacRule{Service: "compute", Resource: "server", Action: "perform", Extra: []string{"*"}} rule_server_list := []string{"compute", "server", "list"} rule_server_perform_start := []string{"compute", "server", "perform", "start"} rule_server_create := []string{"compute", "server", "create"} cases := []struct { inRule SRbacRule inMatch []string want bool count int }{ {all, rule_server_list, true, 0}, {all, rule_server_perform_start, true, 0}, {all, rule_server_create, true, 0}, {compute, rule_server_list, true, 1}, {compute, rule_server_perform_start, true, 1}, {compute, rule_server_create, true, 1}, {getOnly, rule_server_list, false, 0}, {getOnly, rule_server_perform_start, false, 0}, {getOnly, rule_server_create, false, 0}, {listOnly, rule_server_list, true, 1}, {listOnly, rule_server_perform_start, false, 0}, {listOnly, rule_server_create, false, 0}, {serverList, rule_server_list, true, 3}, {serverList, rule_server_perform_start, false, 0}, {serverList, rule_server_create, false, 0}, {serverPerform, rule_server_list, false, 0}, {serverPerform, rule_server_perform_start, true, 3}, {serverPerform, rule_server_create, false, 0}, } for _, c := range cases { got, cnt, _ := c.inRule.match(c.inMatch[0], c.inMatch[1], c.inMatch[2], c.inMatch[3:]...) if got != c.want { t.Errorf("%#v %#v want %#v got %#v", c.inRule, c.inMatch, c.want, got) } if cnt != c.count { t.Errorf("%#v %#v want %#v got %#v", c.inRule, c.inMatch, c.count, cnt) } } } func TestContains(t *testing.T) { cases := []struct { left SRbacRule right SRbacRule contains bool }{ { SRbacRule{Service: "*", Resource: "*", Action: "*", Result: Allow}, SRbacRule{Service: "*", Resource: "*", Action: "*", Result: Allow}, true, }, { SRbacRule{Service: "*", Resource: "*", Action: "*", Result: Allow}, SRbacRule{Service: "compute", Resource: "*", Action: "*", Result: Allow}, true, }, { SRbacRule{Service: "*", Resource: "*", Action: "*", Result: Allow}, SRbacRule{Service: "compute", Resource: "server", Action: "*", Result: Allow}, true, }, { SRbacRule{Service: "*", Resource: "*", Action: "*", Result: Allow}, SRbacRule{Service: "compute", Resource: "server", Action: "list", Result: Allow}, true, }, { SRbacRule{Service: "*", Resource: "*", Action: "*", Result: Allow}, SRbacRule{Service: "compute", Resource: "server", Action: "get", Extra: []string{"vnc"}, Result: Allow}, true, }, { SRbacRule{Service: "compute", Resource: "*", Action: "*", Result: Allow}, SRbacRule{Service: "*", Resource: "*", Action: "*", Result: Allow}, false, }, { SRbacRule{Service: "compute", Resource: "server", Action: "*", Result: Allow}, SRbacRule{Service: "*", Resource: "*", Action: "*", Result: Allow}, false, }, { SRbacRule{Service: "compute", Resource: "server", Action: "list", Result: Allow}, SRbacRule{Service: "*", Resource: "*", Action: "*", Result: Allow}, false, }, { SRbacRule{Service: "compute", Resource: "server", Action: "get", Extra: []string{"vnc"}, Result: Allow}, SRbacRule{Service: "*", Resource: "*", Action: "*", Result: Allow}, false, }, } for _, c := range cases { got := c.left.contains(&c.right) if got != c.contains { t.Errorf("%s contains %s want %#v got %#v", c.left, c.right, c.contains, got) } } } func TestSRabcPolicy_Encode(t *testing.T) { cases := []string{ `{ "condition": "tenant == \"system\" && roles.contains(\"projectowner\")", "is_admin": false, "policy": { "compute": { "keypair": "allow", "server": "deny", "*": { "*": "allow", "create": "deny" } }, "meter": { "*": "allow" } } }`, `{ "auth": false, "ips": ["10.0.0.0/8", "192.168.222.171"], "projects": ["system"], "roles": ["projectowner","admin"], "scope": "domain", "policy": { "compute": { "keypair": "allow", "server": "deny", "*": { "*": "allow", "create": "deny" } }, "meter": { "*": "allow" } } }`, `{ "auth": false, "scope": "domain", "policy": { "compute": { "keypair": "allow", "server": "deny", "*": { "*": "allow", "create": "deny" } }, "meter": { "*": "allow" } } }`, } for _, policyStr := range cases { policyJson, err := jsonutils.ParseString(policyStr) if err != nil { t.Errorf("fail to parse json string %s", err) return } policy := SRbacPolicy{} err = policy.Decode(policyJson) if err != nil { t.Errorf("decode error %s", err) return } policyJson1 := policy.Encode() policy2 := SRbacPolicy{} err = policy2.Decode(policyJson1) if err != nil { t.Errorf("decode error 2 %s", err) return } policyJson2 := policy2.Encode() policyStr1 := policyJson1.PrettyString() policyStr2 := policyJson2.PrettyString() if policyStr1 != policyStr2 { t.Errorf("%s != %s", policyStr1, policyStr2) return } t.Logf("%s", policyStr1) } } /* func TestSRabcPolicy_Explain(t *testing.T) { policyStr := `{ "condition": "usercred.project != \"system\" && usercred.roles==\"projectowner\"", "is_admin": false, "policy": { "compute": { "keypair": "allow", "server": "deny", "*": { "*": "allow", "create": "deny" } }, "meter": { "*": "allow" }, "k8s": "allow" } }` policyJson, err := jsonutils.ParseString(policyStr) if err != nil { t.Errorf("fail to parse json string %s", err) return } policy := SRbacPolicy{} err = policy.Decode(policyJson) if err != nil { t.Errorf("decode error %s", err) return } request := [][]string{ {"compute", "keypair", "list"}, {"compute", "server", "list"}, {"compute", "server", "get", "vnc"}, {"compute", "keypair", "create"}, {"meter", "price", "list"}, {"image", "image", "list"}, {"k8s", "pod", "list"}, } output := policy.Explain(request) t.Logf("%#v", output) } */ func TestConditionParser(t *testing.T) { condition := `tenant=="system" && roles.contains("admin")` tenants := searchMatchTenants(condition) t.Logf("%s", tenants) roles := searchMatchRoles(condition) t.Logf("%s", roles) } func TestSRbacPolicyMatch(t *testing.T) { prefix, _ := netutils.NewIPV4Prefix("10.168.22.0/24") cases := []struct { policy SRbacPolicy userCred IRbacIdentity2 want bool }{ { SRbacPolicy{}, newRbacIdentity2("", "", nil, ""), true, }, { SRbacPolicy{}, nil, true, }, { SRbacPolicy{ Projects: []string{"system"}, }, newRbacIdentity2("", "system", nil, ""), true, }, { SRbacPolicy{ Projects: []string{"system"}, }, newRbacIdentity2("", "demo", nil, ""), false, }, { SRbacPolicy{ Projects: []string{"system"}, Roles: []string{"admin"}, }, newRbacIdentity2("", "system", []string{"admin"}, ""), true, }, { SRbacPolicy{ Projects: []string{"system"}, Roles: []string{"admin"}, }, newRbacIdentity2("", "system", []string{"admin", "_member_"}, ""), true, }, { SRbacPolicy{ Projects: []string{"system"}, Roles: []string{"admin"}, }, newRbacIdentity2("", "system", []string{"_member_"}, ""), false, }, { SRbacPolicy{ Projects: []string{"system"}, Roles: []string{"admin"}, }, nil, false, }, { SRbacPolicy{ Auth: false, }, nil, true, }, { SRbacPolicy{ Projects: []string{"system"}, Roles: []string{"admin"}, Ips: []netutils.IPV4Prefix{prefix}, }, newRbacIdentity2("", "system", []string{"admin"}, "10.0.0.23"), false, }, { SRbacPolicy{ Projects: []string{"system"}, Roles: []string{"admin"}, Ips: []netutils.IPV4Prefix{prefix}, }, newRbacIdentity2("", "system", []string{"admin"}, "10.168.22.23"), true, }, { SRbacPolicy{ Projects: []string{"system"}, Roles: []string{"admin"}, Ips: []netutils.IPV4Prefix{prefix}, }, newRbacIdentity2("", "system", []string{"_member_"}, "10.168.22.23"), false, }, { SRbacPolicy{ Roles: []string{"admin"}, Ips: []netutils.IPV4Prefix{prefix}, }, newRbacIdentity2("", "system", []string{"_member_", "admin"}, "10.168.22.23"), true, }, { SRbacPolicy{ Projects: []string{"system"}, Roles: []string{"admin", "_member_"}, Ips: []netutils.IPV4Prefix{prefix}, }, newRbacIdentity2("", "system", []string{"_member_", "projectowner"}, "10.168.22.23"), true, }, { SRbacPolicy{ Projects: []string{}, Roles: []string{"domain_admin"}, Auth: true, }, newRbacIdentity2("", "ldapproj", []string{"domain_admin"}, ""), true, }, { SRbacPolicy{ Projects: []string{}, Roles: []string{"admin"}, Auth: true, }, newRbacIdentity2("", "", []string{"admin"}, ""), true, }, } for i, c := range cases { got, _ := c.policy.Match(c.userCred) if got != c.want { t.Errorf("[%d]: %#v %#v got %v want %v", i, c.policy, c.userCred, got, c.want) } } } func TestGetMatchRules(t *testing.T) { cases := []struct { rules []SRbacRule service string resource string action string want bool }{ { []SRbacRule{ { Service: "yunionconf", Resource: "parameters", Action: "list", Result: Allow, }, }, "yunionconf", "parameters", "list", true, }, } for _, c := range cases { rule := GetMatchRule(c.rules, c.service, c.resource, c.action) got := rule != nil if got != c.want { t.Errorf("want %v got %v", c.want, got) } } }