Browse code

refactor authorization for sanity

deads2k authored on 2015/02/23 22:27:39
Showing 5 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,115 @@
0
+package authorizer
1
+
2
+import (
3
+	"path"
4
+	"strings"
5
+
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
7
+
8
+	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
9
+)
10
+
11
+type DefaultAuthorizationAttributes struct {
12
+	Verb              string
13
+	Resource          string
14
+	ResourceName      string
15
+	RequestAttributes interface{}
16
+	NonResourceURL    bool
17
+	URL               string
18
+}
19
+
20
+func (a DefaultAuthorizationAttributes) RuleMatches(rule authorizationapi.PolicyRule) (bool, error) {
21
+	if a.IsNonResourceURL() {
22
+		if a.nonResourceMatches(rule) {
23
+			if a.verbMatches(rule.Verbs) {
24
+				return true, nil
25
+			}
26
+		}
27
+
28
+		return false, nil
29
+	}
30
+
31
+	if a.verbMatches(rule.Verbs) {
32
+		allowedResourceTypes := authorizationapi.ExpandResources(rule.Resources)
33
+
34
+		if a.resourceMatches(allowedResourceTypes) {
35
+			if a.nameMatches(rule.ResourceNames) {
36
+				return true, nil
37
+			}
38
+		}
39
+	}
40
+
41
+	return false, nil
42
+}
43
+
44
+func (a DefaultAuthorizationAttributes) verbMatches(verbs util.StringSet) bool {
45
+	return verbs.Has(authorizationapi.VerbAll) || verbs.Has(strings.ToLower(a.GetVerb()))
46
+}
47
+
48
+func (a DefaultAuthorizationAttributes) resourceMatches(allowedResourceTypes util.StringSet) bool {
49
+	return allowedResourceTypes.Has(authorizationapi.ResourceAll) || allowedResourceTypes.Has(strings.ToLower(a.GetResource()))
50
+}
51
+
52
+// nameMatches checks to see if the resourceName of the action is in a the specified whitelist.  An empty whitelist indicates that any name is allowed.
53
+// An empty string in the whitelist should only match the action's resourceName if the resourceName itself is empty string.  This behavior allows for the
54
+// combination of a whitelist for gets in the same rule as a list that won't have a resourceName.  I don't recommend writing such a rule, but we do
55
+// handle it like you'd expect: white list is respected for gets while not preventing the list you explicitly asked for.
56
+func (a DefaultAuthorizationAttributes) nameMatches(allowedResourceNames util.StringSet) bool {
57
+	if len(allowedResourceNames) == 0 {
58
+		return true
59
+	}
60
+
61
+	return allowedResourceNames.Has(a.GetResourceName())
62
+}
63
+
64
+func (a DefaultAuthorizationAttributes) GetVerb() string {
65
+	return a.Verb
66
+}
67
+
68
+// nonResourceMatches take the remainer of a URL and attempts to match it against a series of explicitly allowed steps that can end in a wildcard
69
+func (a DefaultAuthorizationAttributes) nonResourceMatches(rule authorizationapi.PolicyRule) bool {
70
+	for allowedNonResourcePath := range rule.NonResourceURLs {
71
+		// if the allowed resource path ends in a wildcard, check to see if the URL starts with it
72
+		if strings.HasSuffix(allowedNonResourcePath, "*") {
73
+			if strings.HasPrefix(a.GetURL(), allowedNonResourcePath[0:len(allowedNonResourcePath)-1]) {
74
+				return true
75
+			}
76
+		}
77
+
78
+		// if we have an exact match, return true
79
+		if a.GetURL() == allowedNonResourcePath {
80
+			return true
81
+		}
82
+	}
83
+
84
+	return false
85
+}
86
+
87
+// splitPath returns the segments for a URL path.
88
+func splitPath(thePath string) []string {
89
+	thePath = strings.Trim(path.Clean(thePath), "/")
90
+	if thePath == "" {
91
+		return []string{}
92
+	}
93
+	return strings.Split(thePath, "/")
94
+}
95
+
96
+func (a DefaultAuthorizationAttributes) GetResource() string {
97
+	return a.Resource
98
+}
99
+
100
+func (a DefaultAuthorizationAttributes) GetResourceName() string {
101
+	return a.ResourceName
102
+}
103
+
104
+func (a DefaultAuthorizationAttributes) GetRequestAttributes() interface{} {
105
+	return a.RequestAttributes
106
+}
107
+
108
+func (a DefaultAuthorizationAttributes) IsNonResourceURL() bool {
109
+	return a.NonResourceURL
110
+}
111
+
112
+func (a DefaultAuthorizationAttributes) GetURL() string {
113
+	return a.URL
114
+}
0 115
new file mode 100644
... ...
@@ -0,0 +1,53 @@
0
+package authorizer
1
+
2
+import (
3
+	"net/http"
4
+	"strings"
5
+
6
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7
+	kapiserver "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
8
+)
9
+
10
+type openshiftAuthorizationAttributeBuilder struct {
11
+	contextMapper kapi.RequestContextMapper
12
+	infoResolver  *kapiserver.APIRequestInfoResolver
13
+}
14
+
15
+func NewAuthorizationAttributeBuilder(contextMapper kapi.RequestContextMapper, infoResolver *kapiserver.APIRequestInfoResolver) AuthorizationAttributeBuilder {
16
+	return &openshiftAuthorizationAttributeBuilder{contextMapper, infoResolver}
17
+}
18
+
19
+func (a *openshiftAuthorizationAttributeBuilder) GetAttributes(req *http.Request) (AuthorizationAttributes, error) {
20
+	// any url that starts with an API prefix and is more than one step long is considered to be a resource URL.
21
+	// That means that /api is non-resource, /api/v1beta1 is resource, /healthz is non-resource, and /swagger/anything is non-resource
22
+	urlSegments := splitPath(req.URL.Path)
23
+	isResourceURL := (len(urlSegments) > 1) && a.infoResolver.APIPrefixes.Has(urlSegments[0])
24
+
25
+	if !isResourceURL {
26
+		return DefaultAuthorizationAttributes{
27
+			Verb:           strings.ToLower(req.Method),
28
+			NonResourceURL: true,
29
+			URL:            req.URL.Path,
30
+		}, nil
31
+	}
32
+
33
+	requestInfo, err := a.infoResolver.GetAPIRequestInfo(req)
34
+	if err != nil {
35
+		return nil, err
36
+	}
37
+
38
+	// TODO reconsider special casing this.  Having the special case hereallow us to fully share the kube
39
+	// APIRequestInfoResolver without any modification or customization.
40
+	if (requestInfo.Resource == "projects") && (len(requestInfo.Name) > 0) {
41
+		requestInfo.Namespace = requestInfo.Name
42
+	}
43
+
44
+	return DefaultAuthorizationAttributes{
45
+		Verb:              requestInfo.Verb,
46
+		Resource:          requestInfo.Resource,
47
+		ResourceName:      requestInfo.Name,
48
+		RequestAttributes: nil,
49
+		NonResourceURL:    false,
50
+		URL:               req.URL.Path,
51
+	}, nil
52
+}
... ...
@@ -2,42 +2,15 @@ package authorizer
2 2
 
3 3
 import (
4 4
 	"fmt"
5
-	"net/http"
6
-	"path"
7
-	"strings"
8 5
 
9 6
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
10
-	kapiserver "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
11 7
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
12 8
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
13 9
 	kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
14 10
 
15
-	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
16 11
 	"github.com/openshift/origin/pkg/authorization/rulevalidation"
17 12
 )
18 13
 
19
-type Authorizer interface {
20
-	Authorize(ctx kapi.Context, a AuthorizationAttributes) (allowed bool, reason string, err error)
21
-	GetAllowedSubjects(ctx kapi.Context, attributes AuthorizationAttributes) ([]string, []string, error)
22
-}
23
-
24
-type AuthorizationAttributeBuilder interface {
25
-	GetAttributes(request *http.Request) (AuthorizationAttributes, error)
26
-}
27
-
28
-type AuthorizationAttributes interface {
29
-	GetVerb() string
30
-	// GetResource returns the resource type.  If IsNonResourceURL() is true, then GetResource() is "".
31
-	GetResource() string
32
-	GetResourceName() string
33
-	// GetRequestAttributes is of type interface{} because different verbs and different Authorizer/AuthorizationAttributeBuilder pairs may have different contract requirements.
34
-	GetRequestAttributes() interface{}
35
-	// IsNonResourceURL returns true if this is not an action performed against the resource API
36
-	IsNonResourceURL() bool
37
-	// GetURL returns the URL split on '/'s
38
-	GetURL() string
39
-}
40
-
41 14
 type openshiftAuthorizer struct {
42 15
 	masterAuthorizationNamespace string
43 16
 	ruleResolver                 rulevalidation.AuthorizationRuleResolver
... ...
@@ -47,75 +20,39 @@ func NewAuthorizer(masterAuthorizationNamespace string, ruleResolver rulevalidat
47 47
 	return &openshiftAuthorizer{masterAuthorizationNamespace, ruleResolver}
48 48
 }
49 49
 
50
-type DefaultAuthorizationAttributes struct {
51
-	Verb              string
52
-	Resource          string
53
-	ResourceName      string
54
-	RequestAttributes interface{}
55
-	NonResourceURL    bool
56
-	URL               string
57
-}
58
-
59
-type openshiftAuthorizationAttributeBuilder struct {
60
-	contextMapper kapi.RequestContextMapper
61
-	infoResolver  *kapiserver.APIRequestInfoResolver
62
-}
63
-
64
-func NewAuthorizationAttributeBuilder(contextMapper kapi.RequestContextMapper, infoResolver *kapiserver.APIRequestInfoResolver) AuthorizationAttributeBuilder {
65
-	return &openshiftAuthorizationAttributeBuilder{contextMapper, infoResolver}
66
-}
67
-
68
-func doesApplyToUser(ruleUsers, ruleGroups util.StringSet, user user.Info) bool {
69
-	if ruleUsers.Has(user.GetName()) {
70
-		return true
71
-	}
50
+func (a *openshiftAuthorizer) Authorize(ctx kapi.Context, passedAttributes AuthorizationAttributes) (bool, string, error) {
51
+	attributes := coerceToDefaultAuthorizationAttributes(passedAttributes)
72 52
 
73
-	for _, currGroup := range user.GetGroups() {
74
-		if ruleGroups.Has(currGroup) {
75
-			return true
76
-		}
77
-	}
53
+	// keep track of errors in case we are unable to authorize the action.
54
+	// It is entirely possible to get an error and be able to continue determine authorization status in spite of it.
55
+	// This is most common when a bound role is missing, but enough roles are still present and bound to authorize the request.
56
+	errs := []error{}
78 57
 
79
-	return false
80
-}
81
-func contains(list []string, token string) bool {
82
-	for _, curr := range list {
83
-		if curr == token {
84
-			return true
85
-		}
58
+	masterContext := kapi.WithNamespace(ctx, a.masterAuthorizationNamespace)
59
+	globalAllowed, globalReason, err := a.authorizeWithNamespaceRules(masterContext, attributes)
60
+	if globalAllowed {
61
+		return true, globalReason, nil
86 62
 	}
87
-	return false
88
-}
89
-func (a *openshiftAuthorizer) getAllowedSubjectsFromNamespaceBindings(ctx kapi.Context, passedAttributes AuthorizationAttributes) (util.StringSet, util.StringSet, error) {
90
-	attributes := coerceToDefaultAuthorizationAttributes(passedAttributes)
91
-
92
-	roleBindings, err := a.ruleResolver.GetRoleBindings(ctx)
93 63
 	if err != nil {
94
-		return nil, nil, err
64
+		errs = append(errs, err)
95 65
 	}
96 66
 
97
-	users := util.StringSet{}
98
-	groups := util.StringSet{}
99
-	for _, roleBinding := range roleBindings {
100
-		role, err := a.ruleResolver.GetRole(roleBinding)
67
+	namespace, _ := kapi.NamespaceFrom(ctx)
68
+	if len(namespace) != 0 {
69
+		namespaceAllowed, namespaceReason, err := a.authorizeWithNamespaceRules(ctx, attributes)
70
+		if namespaceAllowed {
71
+			return true, namespaceReason, nil
72
+		}
101 73
 		if err != nil {
102
-			return nil, nil, err
74
+			errs = append(errs, err)
103 75
 		}
76
+	}
104 77
 
105
-		for _, rule := range role.Rules {
106
-			matches, err := attributes.RuleMatches(rule)
107
-			if err != nil {
108
-				return nil, nil, err
109
-			}
110
-
111
-			if matches {
112
-				users.Insert(roleBinding.Users.List()...)
113
-				groups.Insert(roleBinding.Groups.List()...)
114
-			}
115
-		}
78
+	if len(errs) > 0 {
79
+		return false, "", kerrors.NewAggregate(errs)
116 80
 	}
117 81
 
118
-	return users, groups, nil
82
+	return false, "denied by default", nil
119 83
 }
120 84
 
121 85
 func (a *openshiftAuthorizer) GetAllowedSubjects(ctx kapi.Context, attributes AuthorizationAttributes) ([]string, []string, error) {
... ...
@@ -140,39 +77,36 @@ func (a *openshiftAuthorizer) GetAllowedSubjects(ctx kapi.Context, attributes Au
140 140
 	return users.List(), groups.List(), nil
141 141
 }
142 142
 
143
-func (a *openshiftAuthorizer) Authorize(ctx kapi.Context, passedAttributes AuthorizationAttributes) (bool, string, error) {
143
+func (a *openshiftAuthorizer) getAllowedSubjectsFromNamespaceBindings(ctx kapi.Context, passedAttributes AuthorizationAttributes) (util.StringSet, util.StringSet, error) {
144 144
 	attributes := coerceToDefaultAuthorizationAttributes(passedAttributes)
145 145
 
146
-	// keep track of errors in case we are unable to authorize the action.
147
-	// It is entirely possible to get an error and be able to continue determine authorization status in spite of it.
148
-	// This is most common when a bound role is missing, but enough roles are still present and bound to authorize the request.
149
-	errs := []error{}
150
-
151
-	masterContext := kapi.WithNamespace(ctx, a.masterAuthorizationNamespace)
152
-	globalAllowed, globalReason, err := a.authorizeWithNamespaceRules(masterContext, attributes)
153
-	if globalAllowed {
154
-		return true, globalReason, nil
155
-	}
146
+	roleBindings, err := a.ruleResolver.GetRoleBindings(ctx)
156 147
 	if err != nil {
157
-		errs = append(errs, err)
148
+		return nil, nil, err
158 149
 	}
159 150
 
160
-	namespace, _ := kapi.NamespaceFrom(ctx)
161
-	if len(namespace) != 0 {
162
-		namespaceAllowed, namespaceReason, err := a.authorizeWithNamespaceRules(ctx, attributes)
163
-		if namespaceAllowed {
164
-			return true, namespaceReason, nil
165
-		}
151
+	users := util.StringSet{}
152
+	groups := util.StringSet{}
153
+	for _, roleBinding := range roleBindings {
154
+		role, err := a.ruleResolver.GetRole(roleBinding)
166 155
 		if err != nil {
167
-			errs = append(errs, err)
156
+			return nil, nil, err
168 157
 		}
169
-	}
170 158
 
171
-	if len(errs) > 0 {
172
-		return false, "", kerrors.NewAggregate(errs)
159
+		for _, rule := range role.Rules {
160
+			matches, err := attributes.RuleMatches(rule)
161
+			if err != nil {
162
+				return nil, nil, err
163
+			}
164
+
165
+			if matches {
166
+				users.Insert(roleBinding.Users.List()...)
167
+				groups.Insert(roleBinding.Groups.List()...)
168
+			}
169
+		}
173 170
 	}
174 171
 
175
-	return false, "denied by default", nil
172
+	return users, groups, nil
176 173
 }
177 174
 
178 175
 // authorizeWithNamespaceRules returns isAllowed, reason, and error.  If an error is returned, isAllowed and reason are still valid.  This seems strange
... ...
@@ -214,358 +148,16 @@ func coerceToDefaultAuthorizationAttributes(passedAttributes AuthorizationAttrib
214 214
 	return attributes
215 215
 }
216 216
 
217
-func (a DefaultAuthorizationAttributes) RuleMatches(rule authorizationapi.PolicyRule) (bool, error) {
218
-	if a.IsNonResourceURL() {
219
-		if a.nonResourceMatches(rule) {
220
-			if a.verbMatches(rule.Verbs) {
221
-				return true, nil
222
-			}
223
-		}
224
-
225
-		return false, nil
226
-	}
227
-
228
-	if a.verbMatches(rule.Verbs) {
229
-		allowedResourceTypes := authorizationapi.ExpandResources(rule.Resources)
230
-
231
-		if a.resourceMatches(allowedResourceTypes) {
232
-			if a.nameMatches(rule.ResourceNames) {
233
-				return true, nil
234
-			}
235
-		}
236
-	}
237
-
238
-	return false, nil
239
-}
240
-
241
-func (a DefaultAuthorizationAttributes) verbMatches(verbs util.StringSet) bool {
242
-	return verbs.Has(authorizationapi.VerbAll) || verbs.Has(strings.ToLower(a.GetVerb()))
243
-}
244
-
245
-func (a DefaultAuthorizationAttributes) resourceMatches(allowedResourceTypes util.StringSet) bool {
246
-	return allowedResourceTypes.Has(authorizationapi.ResourceAll) || allowedResourceTypes.Has(strings.ToLower(a.GetResource()))
247
-}
248
-
249
-// nameMatches checks to see if the resourceName of the action is in a the specified whitelist.  An empty whitelist indicates that any name is allowed.
250
-// An empty string in the whitelist should only match the action's resourceName if the resourceName itself is empty string.  This behavior allows for the
251
-// combination of a whitelist for gets in the same rule as a list that won't have a resourceName.  I don't recommend writing such a rule, but we do
252
-// handle it like you'd expect: white list is respected for gets while not preventing the list you explicitly asked for.
253
-func (a DefaultAuthorizationAttributes) nameMatches(allowedResourceNames util.StringSet) bool {
254
-	if len(allowedResourceNames) == 0 {
217
+func doesApplyToUser(ruleUsers, ruleGroups util.StringSet, user user.Info) bool {
218
+	if ruleUsers.Has(user.GetName()) {
255 219
 		return true
256 220
 	}
257 221
 
258
-	return allowedResourceNames.Has(a.GetResourceName())
259
-}
260
-
261
-func (a DefaultAuthorizationAttributes) GetVerb() string {
262
-	return a.Verb
263
-}
264
-
265
-// nonResourceMatches take the remainer of a URL and attempts to match it against a series of explicitly allowed steps that can end in a wildcard
266
-func (a DefaultAuthorizationAttributes) nonResourceMatches(rule authorizationapi.PolicyRule) bool {
267
-	for allowedNonResourcePath := range rule.NonResourceURLs {
268
-		// if the allowed resource path ends in a wildcard, check to see if the URL starts with it
269
-		if strings.HasSuffix(allowedNonResourcePath, "*") {
270
-			if strings.HasPrefix(a.GetURL(), allowedNonResourcePath[0:len(allowedNonResourcePath)-1]) {
271
-				return true
272
-			}
273
-		}
274
-
275
-		// if we have an exact match, return true
276
-		if a.GetURL() == allowedNonResourcePath {
222
+	for _, currGroup := range user.GetGroups() {
223
+		if ruleGroups.Has(currGroup) {
277 224
 			return true
278 225
 		}
279 226
 	}
280 227
 
281 228
 	return false
282 229
 }
283
-
284
-// splitPath returns the segments for a URL path.
285
-func splitPath(thePath string) []string {
286
-	thePath = strings.Trim(path.Clean(thePath), "/")
287
-	if thePath == "" {
288
-		return []string{}
289
-	}
290
-	return strings.Split(thePath, "/")
291
-}
292
-
293
-func (a DefaultAuthorizationAttributes) GetResource() string {
294
-	return a.Resource
295
-}
296
-
297
-func (a DefaultAuthorizationAttributes) GetResourceName() string {
298
-	return a.ResourceName
299
-}
300
-
301
-func (a DefaultAuthorizationAttributes) GetRequestAttributes() interface{} {
302
-	return a.RequestAttributes
303
-}
304
-
305
-func (a DefaultAuthorizationAttributes) IsNonResourceURL() bool {
306
-	return a.NonResourceURL
307
-}
308
-
309
-func (a DefaultAuthorizationAttributes) GetURL() string {
310
-	return a.URL
311
-}
312
-
313
-func (a *openshiftAuthorizationAttributeBuilder) GetAttributes(req *http.Request) (AuthorizationAttributes, error) {
314
-	// any url that starts with an API prefix and is more than one step long is considered to be a resource URL.
315
-	// That means that /api is non-resource, /api/v1beta1 is resource, /healthz is non-resource, and /swagger/anything is non-resource
316
-	urlSegments := splitPath(req.URL.Path)
317
-	isResourceURL := (len(urlSegments) > 1) && a.infoResolver.APIPrefixes.Has(urlSegments[0])
318
-
319
-	if !isResourceURL {
320
-		return DefaultAuthorizationAttributes{
321
-			Verb:           strings.ToLower(req.Method),
322
-			NonResourceURL: true,
323
-			URL:            req.URL.Path,
324
-		}, nil
325
-	}
326
-
327
-	requestInfo, err := a.infoResolver.GetAPIRequestInfo(req)
328
-	if err != nil {
329
-		return nil, err
330
-	}
331
-
332
-	// TODO reconsider special casing this.  Having the special case hereallow us to fully share the kube
333
-	// APIRequestInfoResolver without any modification or customization.
334
-	if (requestInfo.Resource == "projects") && (len(requestInfo.Name) > 0) {
335
-		requestInfo.Namespace = requestInfo.Name
336
-	}
337
-
338
-	return DefaultAuthorizationAttributes{
339
-		Verb:              requestInfo.Verb,
340
-		Resource:          requestInfo.Resource,
341
-		ResourceName:      requestInfo.Name,
342
-		RequestAttributes: nil,
343
-		NonResourceURL:    false,
344
-		URL:               req.URL.Path,
345
-	}, nil
346
-}
347
-
348
-func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy {
349
-	return &authorizationapi.Policy{
350
-		ObjectMeta: kapi.ObjectMeta{
351
-			Name:              authorizationapi.PolicyName,
352
-			Namespace:         masterNamespace,
353
-			CreationTimestamp: util.Now(),
354
-			UID:               util.NewUUID(),
355
-		},
356
-		LastModified: util.Now(),
357
-		Roles: map[string]authorizationapi.Role{
358
-			"cluster-admin": {
359
-				ObjectMeta: kapi.ObjectMeta{
360
-					Name:      "cluster-admin",
361
-					Namespace: masterNamespace,
362
-				},
363
-				Rules: []authorizationapi.PolicyRule{
364
-					{
365
-						Verbs:     util.NewStringSet(authorizationapi.VerbAll),
366
-						Resources: util.NewStringSet(authorizationapi.ResourceAll),
367
-					},
368
-					{
369
-						Verbs:           util.NewStringSet(authorizationapi.VerbAll),
370
-						NonResourceURLs: util.NewStringSet(authorizationapi.NonResourceAll),
371
-					},
372
-				},
373
-			},
374
-			"admin": {
375
-				ObjectMeta: kapi.ObjectMeta{
376
-					Name:      "admin",
377
-					Namespace: masterNamespace,
378
-				},
379
-				Rules: []authorizationapi.PolicyRule{
380
-					{
381
-						Verbs:     util.NewStringSet("get", "list", "watch", "create", "update", "delete"),
382
-						Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.PermissionGrantingGroupName, authorizationapi.KubeExposedGroupName),
383
-					},
384
-					{
385
-						Verbs:     util.NewStringSet("get", "list", "watch"),
386
-						Resources: util.NewStringSet(authorizationapi.PolicyOwnerGroupName, authorizationapi.KubeAllGroupName),
387
-					},
388
-				},
389
-			},
390
-			"edit": {
391
-				ObjectMeta: kapi.ObjectMeta{
392
-					Name:      "edit",
393
-					Namespace: masterNamespace,
394
-				},
395
-				Rules: []authorizationapi.PolicyRule{
396
-					{
397
-						Verbs:     util.NewStringSet("get", "list", "watch", "create", "update", "delete"),
398
-						Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.KubeExposedGroupName),
399
-					},
400
-					{
401
-						Verbs:     util.NewStringSet("get", "list", "watch"),
402
-						Resources: util.NewStringSet(authorizationapi.KubeAllGroupName),
403
-					},
404
-				},
405
-			},
406
-			"view": {
407
-				ObjectMeta: kapi.ObjectMeta{
408
-					Name:      "view",
409
-					Namespace: masterNamespace,
410
-				},
411
-				Rules: []authorizationapi.PolicyRule{
412
-					{
413
-						Verbs:     util.NewStringSet("get", "list", "watch"),
414
-						Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.KubeAllGroupName),
415
-					},
416
-				},
417
-			},
418
-			"basic-user": {
419
-				ObjectMeta: kapi.ObjectMeta{
420
-					Name:      "view-self",
421
-					Namespace: masterNamespace,
422
-				},
423
-				Rules: []authorizationapi.PolicyRule{
424
-					{Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("users"), ResourceNames: util.NewStringSet("~")},
425
-					{Verbs: util.NewStringSet("list"), Resources: util.NewStringSet("projects")},
426
-				},
427
-			},
428
-			"cluster-status": {
429
-				ObjectMeta: kapi.ObjectMeta{
430
-					Name:      "cluster-status",
431
-					Namespace: masterNamespace,
432
-				},
433
-				Rules: []authorizationapi.PolicyRule{
434
-					{
435
-						Verbs:           util.NewStringSet("get"),
436
-						NonResourceURLs: util.NewStringSet("/healthz", "/version", "/api", "/osapi"),
437
-					},
438
-				},
439
-			},
440
-			"system:deployer": {
441
-				ObjectMeta: kapi.ObjectMeta{
442
-					Name:      "system:deployer",
443
-					Namespace: masterNamespace,
444
-				},
445
-				Rules: []authorizationapi.PolicyRule{
446
-					{
447
-						Verbs:     util.NewStringSet(authorizationapi.VerbAll),
448
-						Resources: util.NewStringSet(authorizationapi.ResourceAll),
449
-					},
450
-				},
451
-			},
452
-			"system:component": {
453
-				ObjectMeta: kapi.ObjectMeta{
454
-					Name:      "system:component",
455
-					Namespace: masterNamespace,
456
-				},
457
-				Rules: []authorizationapi.PolicyRule{
458
-					{
459
-						Verbs:     util.NewStringSet(authorizationapi.VerbAll),
460
-						Resources: util.NewStringSet(authorizationapi.ResourceAll),
461
-					},
462
-				},
463
-			},
464
-			"system:delete-tokens": {
465
-				ObjectMeta: kapi.ObjectMeta{
466
-					Name:      "system:delete-tokens",
467
-					Namespace: masterNamespace,
468
-				},
469
-				Rules: []authorizationapi.PolicyRule{
470
-					{
471
-						Verbs:     util.NewStringSet("delete"),
472
-						Resources: util.NewStringSet("oauthaccesstoken", "oauthauthorizetoken"),
473
-					},
474
-				},
475
-			},
476
-		},
477
-	}
478
-}
479
-
480
-func GetBootstrapPolicyBinding(masterNamespace string) *authorizationapi.PolicyBinding {
481
-	return &authorizationapi.PolicyBinding{
482
-		ObjectMeta: kapi.ObjectMeta{
483
-			Name:              masterNamespace,
484
-			Namespace:         masterNamespace,
485
-			CreationTimestamp: util.Now(),
486
-			UID:               util.NewUUID(),
487
-		},
488
-		LastModified: util.Now(),
489
-		PolicyRef:    kapi.ObjectReference{Namespace: masterNamespace},
490
-		RoleBindings: map[string]authorizationapi.RoleBinding{
491
-			"system:component-binding": {
492
-				ObjectMeta: kapi.ObjectMeta{
493
-					Name:      "system:component-binding",
494
-					Namespace: masterNamespace,
495
-				},
496
-				RoleRef: kapi.ObjectReference{
497
-					Name:      "system:component",
498
-					Namespace: masterNamespace,
499
-				},
500
-				Users: util.NewStringSet("system:openshift-client", "system:kube-client"),
501
-			},
502
-			"system:deployer-binding": {
503
-				ObjectMeta: kapi.ObjectMeta{
504
-					Name:      "system:deployer-binding",
505
-					Namespace: masterNamespace,
506
-				},
507
-				RoleRef: kapi.ObjectReference{
508
-					Name:      "system:deployer",
509
-					Namespace: masterNamespace,
510
-				},
511
-				Users: util.NewStringSet("system:openshift-deployer"),
512
-			},
513
-			"cluster-admin-binding": {
514
-				ObjectMeta: kapi.ObjectMeta{
515
-					Name:      "cluster-admin-binding",
516
-					Namespace: masterNamespace,
517
-				},
518
-				RoleRef: kapi.ObjectReference{
519
-					Name:      "cluster-admin",
520
-					Namespace: masterNamespace,
521
-				},
522
-				Users: util.NewStringSet("system:admin"),
523
-			},
524
-			"basic-user-binding": {
525
-				ObjectMeta: kapi.ObjectMeta{
526
-					Name:      "basic-user-binding",
527
-					Namespace: masterNamespace,
528
-				},
529
-				RoleRef: kapi.ObjectReference{
530
-					Name:      "basic-user",
531
-					Namespace: masterNamespace,
532
-				},
533
-				Groups: util.NewStringSet("system:authenticated"),
534
-			},
535
-			"insecure-cluster-admin-binding": {
536
-				ObjectMeta: kapi.ObjectMeta{
537
-					Name:      "insecure-cluster-admin-binding",
538
-					Namespace: masterNamespace,
539
-				},
540
-				RoleRef: kapi.ObjectReference{
541
-					Name:      "cluster-admin",
542
-					Namespace: masterNamespace,
543
-				},
544
-				// TODO until we decide to enforce policy, simply allow every one access
545
-				Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"),
546
-			},
547
-			"system:delete-tokens-binding": {
548
-				ObjectMeta: kapi.ObjectMeta{
549
-					Name:      "system:delete-tokens-binding",
550
-					Namespace: masterNamespace,
551
-				},
552
-				RoleRef: kapi.ObjectReference{
553
-					Name:      "system:delete-tokens",
554
-					Namespace: masterNamespace,
555
-				},
556
-				Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"),
557
-			},
558
-			"cluster-status-binding": {
559
-				ObjectMeta: kapi.ObjectMeta{
560
-					Name:      "cluster-status-binding",
561
-					Namespace: masterNamespace,
562
-				},
563
-				RoleRef: kapi.ObjectReference{
564
-					Name:      "cluster-status",
565
-					Namespace: masterNamespace,
566
-				},
567
-				Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"),
568
-			},
569
-		},
570
-	}
571
-}
572 230
new file mode 100644
... ...
@@ -0,0 +1,233 @@
0
+package authorizer
1
+
2
+import (
3
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
5
+
6
+	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
7
+)
8
+
9
+func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy {
10
+	return &authorizationapi.Policy{
11
+		ObjectMeta: kapi.ObjectMeta{
12
+			Name:              authorizationapi.PolicyName,
13
+			Namespace:         masterNamespace,
14
+			CreationTimestamp: util.Now(),
15
+			UID:               util.NewUUID(),
16
+		},
17
+		LastModified: util.Now(),
18
+		Roles: map[string]authorizationapi.Role{
19
+			"cluster-admin": {
20
+				ObjectMeta: kapi.ObjectMeta{
21
+					Name:      "cluster-admin",
22
+					Namespace: masterNamespace,
23
+				},
24
+				Rules: []authorizationapi.PolicyRule{
25
+					{
26
+						Verbs:     util.NewStringSet(authorizationapi.VerbAll),
27
+						Resources: util.NewStringSet(authorizationapi.ResourceAll),
28
+					},
29
+					{
30
+						Verbs:           util.NewStringSet(authorizationapi.VerbAll),
31
+						NonResourceURLs: util.NewStringSet(authorizationapi.NonResourceAll),
32
+					},
33
+				},
34
+			},
35
+			"admin": {
36
+				ObjectMeta: kapi.ObjectMeta{
37
+					Name:      "admin",
38
+					Namespace: masterNamespace,
39
+				},
40
+				Rules: []authorizationapi.PolicyRule{
41
+					{
42
+						Verbs:     util.NewStringSet("get", "list", "watch", "create", "update", "delete"),
43
+						Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.PermissionGrantingGroupName, authorizationapi.KubeExposedGroupName),
44
+					},
45
+					{
46
+						Verbs:     util.NewStringSet("get", "list", "watch"),
47
+						Resources: util.NewStringSet(authorizationapi.PolicyOwnerGroupName, authorizationapi.KubeAllGroupName),
48
+					},
49
+				},
50
+			},
51
+			"edit": {
52
+				ObjectMeta: kapi.ObjectMeta{
53
+					Name:      "edit",
54
+					Namespace: masterNamespace,
55
+				},
56
+				Rules: []authorizationapi.PolicyRule{
57
+					{
58
+						Verbs:     util.NewStringSet("get", "list", "watch", "create", "update", "delete"),
59
+						Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.KubeExposedGroupName),
60
+					},
61
+					{
62
+						Verbs:     util.NewStringSet("get", "list", "watch"),
63
+						Resources: util.NewStringSet(authorizationapi.KubeAllGroupName),
64
+					},
65
+				},
66
+			},
67
+			"view": {
68
+				ObjectMeta: kapi.ObjectMeta{
69
+					Name:      "view",
70
+					Namespace: masterNamespace,
71
+				},
72
+				Rules: []authorizationapi.PolicyRule{
73
+					{
74
+						Verbs:     util.NewStringSet("get", "list", "watch"),
75
+						Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.KubeAllGroupName),
76
+					},
77
+				},
78
+			},
79
+			"basic-user": {
80
+				ObjectMeta: kapi.ObjectMeta{
81
+					Name:      "view-self",
82
+					Namespace: masterNamespace,
83
+				},
84
+				Rules: []authorizationapi.PolicyRule{
85
+					{Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("users"), ResourceNames: util.NewStringSet("~")},
86
+					{Verbs: util.NewStringSet("list"), Resources: util.NewStringSet("projects")},
87
+				},
88
+			},
89
+			"cluster-status": {
90
+				ObjectMeta: kapi.ObjectMeta{
91
+					Name:      "cluster-status",
92
+					Namespace: masterNamespace,
93
+				},
94
+				Rules: []authorizationapi.PolicyRule{
95
+					{
96
+						Verbs:           util.NewStringSet("get"),
97
+						NonResourceURLs: util.NewStringSet("/healthz", "/version", "/api", "/osapi"),
98
+					},
99
+				},
100
+			},
101
+			"system:deployer": {
102
+				ObjectMeta: kapi.ObjectMeta{
103
+					Name:      "system:deployer",
104
+					Namespace: masterNamespace,
105
+				},
106
+				Rules: []authorizationapi.PolicyRule{
107
+					{
108
+						Verbs:     util.NewStringSet(authorizationapi.VerbAll),
109
+						Resources: util.NewStringSet(authorizationapi.ResourceAll),
110
+					},
111
+				},
112
+			},
113
+			"system:component": {
114
+				ObjectMeta: kapi.ObjectMeta{
115
+					Name:      "system:component",
116
+					Namespace: masterNamespace,
117
+				},
118
+				Rules: []authorizationapi.PolicyRule{
119
+					{
120
+						Verbs:     util.NewStringSet(authorizationapi.VerbAll),
121
+						Resources: util.NewStringSet(authorizationapi.ResourceAll),
122
+					},
123
+				},
124
+			},
125
+			"system:delete-tokens": {
126
+				ObjectMeta: kapi.ObjectMeta{
127
+					Name:      "system:delete-tokens",
128
+					Namespace: masterNamespace,
129
+				},
130
+				Rules: []authorizationapi.PolicyRule{
131
+					{
132
+						Verbs:     util.NewStringSet("delete"),
133
+						Resources: util.NewStringSet("oauthaccesstoken", "oauthauthorizetoken"),
134
+					},
135
+				},
136
+			},
137
+		},
138
+	}
139
+}
140
+
141
+func GetBootstrapPolicyBinding(masterNamespace string) *authorizationapi.PolicyBinding {
142
+	return &authorizationapi.PolicyBinding{
143
+		ObjectMeta: kapi.ObjectMeta{
144
+			Name:              masterNamespace,
145
+			Namespace:         masterNamespace,
146
+			CreationTimestamp: util.Now(),
147
+			UID:               util.NewUUID(),
148
+		},
149
+		LastModified: util.Now(),
150
+		PolicyRef:    kapi.ObjectReference{Namespace: masterNamespace},
151
+		RoleBindings: map[string]authorizationapi.RoleBinding{
152
+			"system:component-binding": {
153
+				ObjectMeta: kapi.ObjectMeta{
154
+					Name:      "system:component-binding",
155
+					Namespace: masterNamespace,
156
+				},
157
+				RoleRef: kapi.ObjectReference{
158
+					Name:      "system:component",
159
+					Namespace: masterNamespace,
160
+				},
161
+				Users: util.NewStringSet("system:openshift-client", "system:kube-client"),
162
+			},
163
+			"system:deployer-binding": {
164
+				ObjectMeta: kapi.ObjectMeta{
165
+					Name:      "system:deployer-binding",
166
+					Namespace: masterNamespace,
167
+				},
168
+				RoleRef: kapi.ObjectReference{
169
+					Name:      "system:deployer",
170
+					Namespace: masterNamespace,
171
+				},
172
+				Users: util.NewStringSet("system:openshift-deployer"),
173
+			},
174
+			"cluster-admin-binding": {
175
+				ObjectMeta: kapi.ObjectMeta{
176
+					Name:      "cluster-admin-binding",
177
+					Namespace: masterNamespace,
178
+				},
179
+				RoleRef: kapi.ObjectReference{
180
+					Name:      "cluster-admin",
181
+					Namespace: masterNamespace,
182
+				},
183
+				Users: util.NewStringSet("system:admin"),
184
+			},
185
+			"basic-user-binding": {
186
+				ObjectMeta: kapi.ObjectMeta{
187
+					Name:      "basic-user-binding",
188
+					Namespace: masterNamespace,
189
+				},
190
+				RoleRef: kapi.ObjectReference{
191
+					Name:      "basic-user",
192
+					Namespace: masterNamespace,
193
+				},
194
+				Groups: util.NewStringSet("system:authenticated"),
195
+			},
196
+			"insecure-cluster-admin-binding": {
197
+				ObjectMeta: kapi.ObjectMeta{
198
+					Name:      "insecure-cluster-admin-binding",
199
+					Namespace: masterNamespace,
200
+				},
201
+				RoleRef: kapi.ObjectReference{
202
+					Name:      "cluster-admin",
203
+					Namespace: masterNamespace,
204
+				},
205
+				// TODO until we decide to enforce policy, simply allow every one access
206
+				Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"),
207
+			},
208
+			"system:delete-tokens-binding": {
209
+				ObjectMeta: kapi.ObjectMeta{
210
+					Name:      "system:delete-tokens-binding",
211
+					Namespace: masterNamespace,
212
+				},
213
+				RoleRef: kapi.ObjectReference{
214
+					Name:      "system:delete-tokens",
215
+					Namespace: masterNamespace,
216
+				},
217
+				Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"),
218
+			},
219
+			"cluster-status-binding": {
220
+				ObjectMeta: kapi.ObjectMeta{
221
+					Name:      "cluster-status-binding",
222
+					Namespace: masterNamespace,
223
+				},
224
+				RoleRef: kapi.ObjectReference{
225
+					Name:      "cluster-status",
226
+					Namespace: masterNamespace,
227
+				},
228
+				Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"),
229
+			},
230
+		},
231
+	}
232
+}
0 233
new file mode 100644
... ...
@@ -0,0 +1,29 @@
0
+package authorizer
1
+
2
+import (
3
+	"net/http"
4
+
5
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
6
+)
7
+
8
+type Authorizer interface {
9
+	Authorize(ctx kapi.Context, a AuthorizationAttributes) (allowed bool, reason string, err error)
10
+	GetAllowedSubjects(ctx kapi.Context, attributes AuthorizationAttributes) ([]string, []string, error)
11
+}
12
+
13
+type AuthorizationAttributeBuilder interface {
14
+	GetAttributes(request *http.Request) (AuthorizationAttributes, error)
15
+}
16
+
17
+type AuthorizationAttributes interface {
18
+	GetVerb() string
19
+	// GetResource returns the resource type.  If IsNonResourceURL() is true, then GetResource() is "".
20
+	GetResource() string
21
+	GetResourceName() string
22
+	// GetRequestAttributes is of type interface{} because different verbs and different Authorizer/AuthorizationAttributeBuilder pairs may have different contract requirements.
23
+	GetRequestAttributes() interface{}
24
+	// IsNonResourceURL returns true if this is not an action performed against the resource API
25
+	IsNonResourceURL() bool
26
+	// GetURL returns the URL path being requested, including the leading '/'
27
+	GetURL() string
28
+}