1
16
17 package subjectaccessreview
18
19 import (
20 "context"
21 "errors"
22 "reflect"
23 "strings"
24 "testing"
25
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apiserver/pkg/authentication/user"
28 "k8s.io/apiserver/pkg/authorization/authorizer"
29 genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
30 "k8s.io/apiserver/pkg/registry/rest"
31 authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
32 )
33
34 type fakeAuthorizer struct {
35 attrs authorizer.Attributes
36
37 decision authorizer.Decision
38 reason string
39 err error
40 }
41
42 func (f *fakeAuthorizer) Authorize(ctx context.Context, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
43 f.attrs = attrs
44 return f.decision, f.reason, f.err
45 }
46
47 func TestCreate(t *testing.T) {
48 testcases := map[string]struct {
49 spec authorizationapi.SubjectAccessReviewSpec
50 decision authorizer.Decision
51 reason string
52 err error
53
54 expectedErr string
55 expectedAttrs authorizer.Attributes
56 expectedStatus authorizationapi.SubjectAccessReviewStatus
57 }{
58 "empty": {
59 expectedErr: "nonResourceAttributes or resourceAttributes",
60 },
61
62 "nonresource rejected": {
63 spec: authorizationapi.SubjectAccessReviewSpec{
64 User: "bob",
65 NonResourceAttributes: &authorizationapi.NonResourceAttributes{Verb: "get", Path: "/mypath"},
66 },
67 decision: authorizer.DecisionNoOpinion,
68 reason: "myreason",
69 err: errors.New("myerror"),
70 expectedAttrs: authorizer.AttributesRecord{
71 User: &user.DefaultInfo{Name: "bob"},
72 Verb: "get",
73 Path: "/mypath",
74 ResourceRequest: false,
75 },
76 expectedStatus: authorizationapi.SubjectAccessReviewStatus{
77 Allowed: false,
78 Reason: "myreason",
79 EvaluationError: "myerror",
80 },
81 },
82
83 "nonresource allowed": {
84 spec: authorizationapi.SubjectAccessReviewSpec{
85 User: "bob",
86 NonResourceAttributes: &authorizationapi.NonResourceAttributes{Verb: "get", Path: "/mypath"},
87 },
88 decision: authorizer.DecisionAllow,
89 reason: "allowed",
90 err: nil,
91 expectedAttrs: authorizer.AttributesRecord{
92 User: &user.DefaultInfo{Name: "bob"},
93 Verb: "get",
94 Path: "/mypath",
95 ResourceRequest: false,
96 },
97 expectedStatus: authorizationapi.SubjectAccessReviewStatus{
98 Allowed: true,
99 Reason: "allowed",
100 EvaluationError: "",
101 },
102 },
103
104 "resource rejected": {
105 spec: authorizationapi.SubjectAccessReviewSpec{
106 User: "bob",
107 ResourceAttributes: &authorizationapi.ResourceAttributes{
108 Namespace: "myns",
109 Verb: "create",
110 Group: "extensions",
111 Version: "v1beta1",
112 Resource: "deployments",
113 Subresource: "scale",
114 Name: "mydeployment",
115 },
116 },
117 decision: authorizer.DecisionNoOpinion,
118 reason: "myreason",
119 err: errors.New("myerror"),
120 expectedAttrs: authorizer.AttributesRecord{
121 User: &user.DefaultInfo{Name: "bob"},
122 Namespace: "myns",
123 Verb: "create",
124 APIGroup: "extensions",
125 APIVersion: "v1beta1",
126 Resource: "deployments",
127 Subresource: "scale",
128 Name: "mydeployment",
129 ResourceRequest: true,
130 },
131 expectedStatus: authorizationapi.SubjectAccessReviewStatus{
132 Allowed: false,
133 Denied: false,
134 Reason: "myreason",
135 EvaluationError: "myerror",
136 },
137 },
138
139 "resource allowed": {
140 spec: authorizationapi.SubjectAccessReviewSpec{
141 User: "bob",
142 ResourceAttributes: &authorizationapi.ResourceAttributes{
143 Namespace: "myns",
144 Verb: "create",
145 Group: "extensions",
146 Version: "v1beta1",
147 Resource: "deployments",
148 Subresource: "scale",
149 Name: "mydeployment",
150 },
151 },
152 decision: authorizer.DecisionAllow,
153 reason: "allowed",
154 err: nil,
155 expectedAttrs: authorizer.AttributesRecord{
156 User: &user.DefaultInfo{Name: "bob"},
157 Namespace: "myns",
158 Verb: "create",
159 APIGroup: "extensions",
160 APIVersion: "v1beta1",
161 Resource: "deployments",
162 Subresource: "scale",
163 Name: "mydeployment",
164 ResourceRequest: true,
165 },
166 expectedStatus: authorizationapi.SubjectAccessReviewStatus{
167 Allowed: true,
168 Denied: false,
169 Reason: "allowed",
170 EvaluationError: "",
171 },
172 },
173
174 "resource denied": {
175 spec: authorizationapi.SubjectAccessReviewSpec{
176 User: "bob",
177 ResourceAttributes: &authorizationapi.ResourceAttributes{},
178 },
179 decision: authorizer.DecisionDeny,
180 expectedAttrs: authorizer.AttributesRecord{
181 User: &user.DefaultInfo{Name: "bob"},
182 ResourceRequest: true,
183 APIVersion: "*",
184 },
185 expectedStatus: authorizationapi.SubjectAccessReviewStatus{
186 Allowed: false,
187 Denied: true,
188 },
189 },
190 }
191
192 for k, tc := range testcases {
193 auth := &fakeAuthorizer{
194 decision: tc.decision,
195 reason: tc.reason,
196 err: tc.err,
197 }
198 storage := NewREST(auth)
199
200 result, err := storage.Create(genericapirequest.NewContext(), &authorizationapi.SubjectAccessReview{Spec: tc.spec}, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
201 if err != nil {
202 if tc.expectedErr != "" {
203 if !strings.Contains(err.Error(), tc.expectedErr) {
204 t.Errorf("%s: expected %s to contain %q", k, err, tc.expectedErr)
205 }
206 } else {
207 t.Errorf("%s: %v", k, err)
208 }
209 continue
210 }
211 if !reflect.DeepEqual(auth.attrs, tc.expectedAttrs) {
212 t.Errorf("%s: expected\n%#v\ngot\n%#v", k, tc.expectedAttrs, auth.attrs)
213 }
214 status := result.(*authorizationapi.SubjectAccessReview).Status
215 if !reflect.DeepEqual(status, tc.expectedStatus) {
216 t.Errorf("%s: expected\n%#v\ngot\n%#v", k, tc.expectedStatus, status)
217 }
218 }
219 }
220
View as plain text