Skip to content
This repository was archived by the owner on Mar 22, 2018. It is now read-only.

Commit e7f4582

Browse files
committed
Unit tests for authentication and authorisation
1 parent 6d1ddef commit e7f4582

File tree

3 files changed

+252
-0
lines changed

3 files changed

+252
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package keystone
18+
19+
import (
20+
"os"
21+
"reflect"
22+
"testing"
23+
24+
"github.com/gophercloud/gophercloud"
25+
26+
"github.com/gophercloud/gophercloud/openstack"
27+
"k8s.io/apiserver/pkg/authentication/user"
28+
)
29+
30+
func newKeystoneAuthenticator(authURL string, client *gophercloud.ServiceClient) KeystoneAuthenticator {
31+
32+
return KeystoneAuthenticator{
33+
authURL: authURL,
34+
client: client,
35+
}
36+
37+
}
38+
39+
func TestAuthenticateToken(t *testing.T) {
40+
var (
41+
calledWithToken []string
42+
43+
resultUsers map[string]user.Info
44+
)
45+
46+
authUrl := os.Getenv("OS_AUTH_URL")
47+
48+
provider, _ := openstack.NewClient(authUrl)
49+
cli := &gophercloud.ServiceClient{
50+
ProviderClient: provider,
51+
Endpoint: authUrl,
52+
}
53+
54+
a := newKeystoneAuthenticator(authUrl, cli)
55+
56+
calledWithToken, resultUsers = []string{}, nil
57+
a.AuthenticateToken("bad1")
58+
a.AuthenticateToken("bad2")
59+
a.AuthenticateToken("bad3")
60+
a.AuthenticateToken("bad1")
61+
a.AuthenticateToken("bad2")
62+
a.AuthenticateToken("bad3")
63+
if !reflect.DeepEqual(calledWithToken, []string{"bad1", "bad2", "bad3", "bad1", "bad2", "bad3"}) {
64+
t.Errorf("Expected failing calls to bypass cache, got %v", calledWithToken)
65+
}
66+
67+
// reset calls, make the backend return success for three user tokens
68+
calledWithToken = []string{}
69+
resultUsers = map[string]user.Info{}
70+
resultUsers["usertoken1"] = &user.DefaultInfo{Name: "user1"}
71+
resultUsers["usertoken2"] = &user.DefaultInfo{Name: "user2"}
72+
resultUsers["usertoken3"] = &user.DefaultInfo{Name: "user3"}
73+
74+
if user, ok, err := a.AuthenticateToken("usertoken1"); err != nil || !ok || user.GetName() != "user1" {
75+
t.Errorf("Expected user1")
76+
}
77+
if user, ok, err := a.AuthenticateToken("usertoken2"); err != nil || !ok || user.GetName() != "user2" {
78+
t.Errorf("Expected user2")
79+
}
80+
if user, ok, err := a.AuthenticateToken("usertoken3"); err != nil || !ok || user.GetName() != "user3" {
81+
t.Errorf("Expected user3")
82+
}
83+
if !reflect.DeepEqual(calledWithToken, []string{"usertoken1", "usertoken2", "usertoken3"}) {
84+
t.Errorf("Expected token calls, got %v", calledWithToken)
85+
}
86+
87+
// reset calls, make the backend return failures
88+
calledWithToken = []string{}
89+
resultUsers = nil
90+
91+
// authenticate calls still succeed and backend is not hit
92+
if user, ok, err := a.AuthenticateToken("usertoken1"); err != nil || !ok || user.GetName() != "user1" {
93+
t.Errorf("Expected user1")
94+
}
95+
if user, ok, err := a.AuthenticateToken("usertoken2"); err != nil || !ok || user.GetName() != "user2" {
96+
t.Errorf("Expected user2")
97+
}
98+
if user, ok, err := a.AuthenticateToken("usertoken3"); err != nil || !ok || user.GetName() != "user3" {
99+
t.Errorf("Expected user3")
100+
}
101+
if !reflect.DeepEqual(calledWithToken, []string{}) {
102+
t.Errorf("Expected no token calls, got %v", calledWithToken)
103+
}
104+
105+
// backend is consulted again and fails
106+
a.AuthenticateToken("usertoken1")
107+
a.AuthenticateToken("usertoken2")
108+
a.AuthenticateToken("usertoken3")
109+
if !reflect.DeepEqual(calledWithToken, []string{"usertoken1", "usertoken2", "usertoken3"}) {
110+
t.Errorf("Expected token calls, got %v", calledWithToken)
111+
}
112+
}

pkg/identity/keystone/authorizer.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ type KeystoneAuthorizer struct {
3131
pl PolicyList
3232
}
3333

34+
// NewAuthorizer returns a new authorizer
35+
func NewAuthorizer(authURL string, client *gophercloud.ServiceClient, pl PolicyList) authorizer.Authorizer {
36+
return &KeystoneAuthorizer{
37+
authURL: authURL,
38+
client: client,
39+
pl: pl,
40+
}
41+
}
42+
3443
func resourceMatches(p Policy, a authorizer.Attributes) bool {
3544
if p.NonResourceSpec != nil && p.ResourceSpec != nil {
3645
glog.Infof("Policy has both resource and nonresource sections. skipping : %#v", p)
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package keystone
18+
19+
import (
20+
"testing"
21+
22+
"os"
23+
24+
"github.com/gophercloud/gophercloud"
25+
"github.com/gophercloud/gophercloud/openstack"
26+
"k8s.io/apiserver/pkg/authentication/user"
27+
"k8s.io/apiserver/pkg/authorization/authorizer"
28+
)
29+
30+
func TestAuthorizer(t *testing.T) {
31+
32+
authUrl := os.Getenv("OS_AUTH_URL")
33+
34+
provider, _ := openstack.NewClient(authUrl)
35+
cli := &gophercloud.ServiceClient{
36+
ProviderClient: provider,
37+
Endpoint: authUrl,
38+
}
39+
path := os.Getenv("OS_POLICY_PATH")
40+
policy, _ := NewFromFile(path)
41+
authz := NewAuthorizer(authUrl, cli, policy).(*KeystoneAuthorizer)
42+
43+
node0 := &user.DefaultInfo{Name: "system:node:node0", Groups: []string{"system:nodes"}}
44+
45+
tests := []struct {
46+
name string
47+
attrs authorizer.AttributesRecord
48+
expect authorizer.Decision
49+
}{
50+
{
51+
name: "allowed configmap",
52+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node0", Namespace: "ns0"},
53+
expect: authorizer.DecisionAllow,
54+
},
55+
{
56+
name: "allowed secret via pod",
57+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-pod0-node0", Namespace: "ns0"},
58+
expect: authorizer.DecisionAllow,
59+
},
60+
{
61+
name: "allowed shared secret via pod",
62+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-shared", Namespace: "ns0"},
63+
expect: authorizer.DecisionAllow,
64+
},
65+
{
66+
name: "allowed shared secret via pvc",
67+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret-pv0-pod0-node0-ns0", Namespace: "ns0"},
68+
expect: authorizer.DecisionAllow,
69+
},
70+
{
71+
name: "allowed pvc",
72+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumeclaims", Name: "pvc0-pod0-node0", Namespace: "ns0"},
73+
expect: authorizer.DecisionAllow,
74+
},
75+
{
76+
name: "allowed pv",
77+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumes", Name: "pv0-pod0-node0-ns0", Namespace: ""},
78+
expect: authorizer.DecisionAllow,
79+
},
80+
81+
{
82+
name: "disallowed configmap",
83+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node1", Namespace: "ns0"},
84+
expect: authorizer.DecisionNoOpinion,
85+
},
86+
{
87+
name: "disallowed secret via pod",
88+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-pod0-node1", Namespace: "ns0"},
89+
expect: authorizer.DecisionNoOpinion,
90+
},
91+
{
92+
name: "disallowed shared secret via pvc",
93+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret-pv0-pod0-node1-ns0", Namespace: "ns0"},
94+
expect: authorizer.DecisionNoOpinion,
95+
},
96+
{
97+
name: "disallowed pvc",
98+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumeclaims", Name: "pvc0-pod0-node1", Namespace: "ns0"},
99+
expect: authorizer.DecisionNoOpinion,
100+
},
101+
{
102+
name: "disallowed pv",
103+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumes", Name: "pv0-pod0-node1-ns0", Namespace: ""},
104+
expect: authorizer.DecisionNoOpinion,
105+
},
106+
{
107+
name: "disallowed attachment - no relationship",
108+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "volumeattachments", APIGroup: "storage.k8s.io", Name: "attachment0-node1"},
109+
expect: authorizer.DecisionNoOpinion,
110+
},
111+
{
112+
name: "disallowed attachment - feature disabled",
113+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "volumeattachments", APIGroup: "storage.k8s.io", Name: "attachment0-node0"},
114+
expect: authorizer.DecisionNoOpinion,
115+
},
116+
{
117+
name: "allowed attachment - feature enabled",
118+
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "volumeattachments", APIGroup: "storage.k8s.io", Name: "attachment0-node0"},
119+
expect: authorizer.DecisionAllow,
120+
},
121+
}
122+
123+
for _, tc := range tests {
124+
t.Run(tc.name, func(t *testing.T) {
125+
decision, _, _ := authz.Authorize(tc.attrs)
126+
if decision != tc.expect {
127+
t.Errorf("expected %v, got %v", tc.expect, decision)
128+
}
129+
})
130+
}
131+
}

0 commit comments

Comments
 (0)