mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	add deny to SAR API
This commit is contained in:
		@@ -142,6 +142,11 @@ type SelfSubjectAccessReviewSpec struct {
 | 
			
		||||
type SubjectAccessReviewStatus struct {
 | 
			
		||||
	// Allowed is required. True if the action would be allowed, false otherwise.
 | 
			
		||||
	Allowed bool
 | 
			
		||||
	// Denied is optional. True if the action would be denied, otherwise
 | 
			
		||||
	// false. If both allowed is false and denied is false, then the
 | 
			
		||||
	// authorizer has no opinion on whether to authorize the action. Denied
 | 
			
		||||
	// may not be true if Allowed is true.
 | 
			
		||||
	Denied bool
 | 
			
		||||
	// Reason is optional.  It indicates why a request was allowed or denied.
 | 
			
		||||
	Reason string
 | 
			
		||||
	// EvaluationError is an indication that some error occurred during the authorization check.
 | 
			
		||||
 
 | 
			
		||||
@@ -62,6 +62,7 @@ func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, createV
 | 
			
		||||
 | 
			
		||||
	localSubjectAccessReview.Status = authorizationapi.SubjectAccessReviewStatus{
 | 
			
		||||
		Allowed: (decision == authorizer.DecisionAllow),
 | 
			
		||||
		Denied:  (decision == authorizer.DecisionDeny),
 | 
			
		||||
		Reason:  reason,
 | 
			
		||||
	}
 | 
			
		||||
	if evaluationErr != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -65,6 +65,7 @@ func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, createV
 | 
			
		||||
 | 
			
		||||
	selfSAR.Status = authorizationapi.SubjectAccessReviewStatus{
 | 
			
		||||
		Allowed: (decision == authorizer.DecisionAllow),
 | 
			
		||||
		Denied:  (decision == authorizer.DecisionDeny),
 | 
			
		||||
		Reason:  reason,
 | 
			
		||||
	}
 | 
			
		||||
	if evaluationErr != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -55,6 +55,7 @@ func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, createV
 | 
			
		||||
 | 
			
		||||
	subjectAccessReview.Status = authorizationapi.SubjectAccessReviewStatus{
 | 
			
		||||
		Allowed: (decision == authorizer.DecisionAllow),
 | 
			
		||||
		Denied:  (decision == authorizer.DecisionDeny),
 | 
			
		||||
		Reason:  reason,
 | 
			
		||||
	}
 | 
			
		||||
	if evaluationErr != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -33,23 +33,20 @@ import (
 | 
			
		||||
type fakeAuthorizer struct {
 | 
			
		||||
	attrs authorizer.Attributes
 | 
			
		||||
 | 
			
		||||
	ok     bool
 | 
			
		||||
	decision authorizer.Decision
 | 
			
		||||
	reason   string
 | 
			
		||||
	err      error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *fakeAuthorizer) Authorize(attrs authorizer.Attributes) (authorizer.Decision, string, error) {
 | 
			
		||||
	f.attrs = attrs
 | 
			
		||||
	if f.ok {
 | 
			
		||||
		return authorizer.DecisionAllow, f.reason, f.err
 | 
			
		||||
	}
 | 
			
		||||
	return authorizer.DecisionNoOpinion, f.reason, f.err
 | 
			
		||||
	return f.decision, f.reason, f.err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCreate(t *testing.T) {
 | 
			
		||||
	testcases := map[string]struct {
 | 
			
		||||
		spec     authorizationapi.SubjectAccessReviewSpec
 | 
			
		||||
		ok     bool
 | 
			
		||||
		decision authorizer.Decision
 | 
			
		||||
		reason   string
 | 
			
		||||
		err      error
 | 
			
		||||
 | 
			
		||||
@@ -66,7 +63,7 @@ func TestCreate(t *testing.T) {
 | 
			
		||||
				User: "bob",
 | 
			
		||||
				NonResourceAttributes: &authorizationapi.NonResourceAttributes{Verb: "get", Path: "/mypath"},
 | 
			
		||||
			},
 | 
			
		||||
			ok:     false,
 | 
			
		||||
			decision: authorizer.DecisionNoOpinion,
 | 
			
		||||
			reason:   "myreason",
 | 
			
		||||
			err:      errors.New("myerror"),
 | 
			
		||||
			expectedAttrs: authorizer.AttributesRecord{
 | 
			
		||||
@@ -87,7 +84,7 @@ func TestCreate(t *testing.T) {
 | 
			
		||||
				User: "bob",
 | 
			
		||||
				NonResourceAttributes: &authorizationapi.NonResourceAttributes{Verb: "get", Path: "/mypath"},
 | 
			
		||||
			},
 | 
			
		||||
			ok:     true,
 | 
			
		||||
			decision: authorizer.DecisionAllow,
 | 
			
		||||
			reason:   "allowed",
 | 
			
		||||
			err:      nil,
 | 
			
		||||
			expectedAttrs: authorizer.AttributesRecord{
 | 
			
		||||
@@ -116,7 +113,7 @@ func TestCreate(t *testing.T) {
 | 
			
		||||
					Name:        "mydeployment",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			ok:     false,
 | 
			
		||||
			decision: authorizer.DecisionNoOpinion,
 | 
			
		||||
			reason:   "myreason",
 | 
			
		||||
			err:      errors.New("myerror"),
 | 
			
		||||
			expectedAttrs: authorizer.AttributesRecord{
 | 
			
		||||
@@ -132,6 +129,7 @@ func TestCreate(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			expectedStatus: authorizationapi.SubjectAccessReviewStatus{
 | 
			
		||||
				Allowed:         false,
 | 
			
		||||
				Denied:          false,
 | 
			
		||||
				Reason:          "myreason",
 | 
			
		||||
				EvaluationError: "myerror",
 | 
			
		||||
			},
 | 
			
		||||
@@ -150,7 +148,7 @@ func TestCreate(t *testing.T) {
 | 
			
		||||
					Name:        "mydeployment",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			ok:     true,
 | 
			
		||||
			decision: authorizer.DecisionAllow,
 | 
			
		||||
			reason:   "allowed",
 | 
			
		||||
			err:      nil,
 | 
			
		||||
			expectedAttrs: authorizer.AttributesRecord{
 | 
			
		||||
@@ -166,15 +164,32 @@ func TestCreate(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			expectedStatus: authorizationapi.SubjectAccessReviewStatus{
 | 
			
		||||
				Allowed:         true,
 | 
			
		||||
				Denied:          false,
 | 
			
		||||
				Reason:          "allowed",
 | 
			
		||||
				EvaluationError: "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		"resource denied": {
 | 
			
		||||
			spec: authorizationapi.SubjectAccessReviewSpec{
 | 
			
		||||
				User:               "bob",
 | 
			
		||||
				ResourceAttributes: &authorizationapi.ResourceAttributes{},
 | 
			
		||||
			},
 | 
			
		||||
			decision: authorizer.DecisionDeny,
 | 
			
		||||
			expectedAttrs: authorizer.AttributesRecord{
 | 
			
		||||
				User:            &user.DefaultInfo{Name: "bob"},
 | 
			
		||||
				ResourceRequest: true,
 | 
			
		||||
			},
 | 
			
		||||
			expectedStatus: authorizationapi.SubjectAccessReviewStatus{
 | 
			
		||||
				Allowed: false,
 | 
			
		||||
				Denied:  true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, tc := range testcases {
 | 
			
		||||
		auth := &fakeAuthorizer{
 | 
			
		||||
			ok:     tc.ok,
 | 
			
		||||
			decision: tc.decision,
 | 
			
		||||
			reason:   tc.reason,
 | 
			
		||||
			err:      tc.err,
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -171,6 +171,12 @@ type SelfSubjectAccessReviewSpec struct {
 | 
			
		||||
type SubjectAccessReviewStatus struct {
 | 
			
		||||
	// Allowed is required. True if the action would be allowed, false otherwise.
 | 
			
		||||
	Allowed bool `json:"allowed" protobuf:"varint,1,opt,name=allowed"`
 | 
			
		||||
	// Denied is optional. True if the action would be denied, otherwise
 | 
			
		||||
	// false. If both allowed is false and denied is false, then the
 | 
			
		||||
	// authorizer has no opinion on whether to authorize the action. Denied
 | 
			
		||||
	// may not be true if Allowed is true.
 | 
			
		||||
	// +optional
 | 
			
		||||
	Denied bool `json:"denied,omitempty" protobuf:"varint,4,opt,name=denied"`
 | 
			
		||||
	// Reason is optional.  It indicates why a request was allowed or denied.
 | 
			
		||||
	// +optional
 | 
			
		||||
	Reason string `json:"reason,omitempty" protobuf:"bytes,2,opt,name=reason"`
 | 
			
		||||
 
 | 
			
		||||
@@ -171,6 +171,12 @@ type SelfSubjectAccessReviewSpec struct {
 | 
			
		||||
type SubjectAccessReviewStatus struct {
 | 
			
		||||
	// Allowed is required. True if the action would be allowed, false otherwise.
 | 
			
		||||
	Allowed bool `json:"allowed" protobuf:"varint,1,opt,name=allowed"`
 | 
			
		||||
	// Denied is optional. True if the action would be denied, otherwise
 | 
			
		||||
	// false. If both allowed is false and denied is false, then the
 | 
			
		||||
	// authorizer has no opinion on whether to authorize the action. Denied
 | 
			
		||||
	// may not be true if Allowed is true.
 | 
			
		||||
	// +optional
 | 
			
		||||
	Denied bool `json:"denied,omitempty" protobuf:"varint,4,opt,name=denied"`
 | 
			
		||||
	// Reason is optional.  It indicates why a request was allowed or denied.
 | 
			
		||||
	// +optional
 | 
			
		||||
	Reason string `json:"reason,omitempty" protobuf:"bytes,2,opt,name=reason"`
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,7 @@ type WebhookAuthorizer struct {
 | 
			
		||||
	authorizedTTL       time.Duration
 | 
			
		||||
	unauthorizedTTL     time.Duration
 | 
			
		||||
	initialBackoff      time.Duration
 | 
			
		||||
	decisionOnError     authorizer.Decision
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewFromInterface creates a WebhookAuthorizer using the given subjectAccessReview client
 | 
			
		||||
@@ -93,6 +94,7 @@ func newWithBackoff(subjectAccessReview authorizationclient.SubjectAccessReviewI
 | 
			
		||||
		authorizedTTL:       authorizedTTL,
 | 
			
		||||
		unauthorizedTTL:     unauthorizedTTL,
 | 
			
		||||
		initialBackoff:      initialBackoff,
 | 
			
		||||
		decisionOnError:     authorizer.DecisionNoOpinion,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -140,9 +142,9 @@ func newWithBackoff(subjectAccessReview authorizationclient.SubjectAccessReviewI
 | 
			
		||||
//       }
 | 
			
		||||
//     }
 | 
			
		||||
//
 | 
			
		||||
// TODO(mikedanese): We should eventually fail closed when we encounter and
 | 
			
		||||
// error. We are failing open now to preserve backwards compatible behavior.
 | 
			
		||||
// Fix this after deprecation.
 | 
			
		||||
// TODO(mikedanese): We should eventually support failing closed when we
 | 
			
		||||
// encounter an error. We are failing open now to preserve backwards compatible
 | 
			
		||||
// behavior.
 | 
			
		||||
func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (decision authorizer.Decision, reason string, err error) {
 | 
			
		||||
	r := &authorization.SubjectAccessReview{}
 | 
			
		||||
	if user := attr.GetUser(); user != nil {
 | 
			
		||||
@@ -172,7 +174,7 @@ func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (decision auth
 | 
			
		||||
	}
 | 
			
		||||
	key, err := json.Marshal(r.Spec)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return authorizer.DecisionNoOpinion, "", err
 | 
			
		||||
		return w.decisionOnError, "", err
 | 
			
		||||
	}
 | 
			
		||||
	if entry, ok := w.responseCache.Get(string(key)); ok {
 | 
			
		||||
		r.Status = entry.(authorization.SubjectAccessReviewStatus)
 | 
			
		||||
@@ -188,7 +190,7 @@ func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (decision auth
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// An error here indicates bad configuration or an outage. Log for debugging.
 | 
			
		||||
			glog.Errorf("Failed to make webhook authorizer request: %v", err)
 | 
			
		||||
			return authorizer.DecisionNoOpinion, "", err
 | 
			
		||||
			return w.decisionOnError, "", err
 | 
			
		||||
		}
 | 
			
		||||
		r.Status = result.Status
 | 
			
		||||
		if r.Status.Allowed {
 | 
			
		||||
@@ -197,9 +199,14 @@ func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (decision auth
 | 
			
		||||
			w.responseCache.Add(string(key), r.Status, w.unauthorizedTTL)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if r.Status.Allowed {
 | 
			
		||||
	switch {
 | 
			
		||||
	case r.Status.Denied && r.Status.Allowed:
 | 
			
		||||
		return authorizer.DecisionDeny, r.Status.Reason, fmt.Errorf("webhook subject access review returned both allow and deny response")
 | 
			
		||||
	case r.Status.Denied:
 | 
			
		||||
		return authorizer.DecisionDeny, r.Status.Reason, nil
 | 
			
		||||
	case r.Status.Allowed:
 | 
			
		||||
		return authorizer.DecisionAllow, r.Status.Reason, nil
 | 
			
		||||
	} else {
 | 
			
		||||
	default:
 | 
			
		||||
		return authorizer.DecisionNoOpinion, r.Status.Reason, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user