From 5cbdfea7ce069d8090b8e9ac5fb3084c5e6fe9b6 Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Thu, 14 Nov 2024 23:15:47 -0800 Subject: [PATCH 1/5] fix: add authorizationGroups property to K8s SAR authorization, fixes #506 Signed-off-by: Dhiraj Bokde --- api/v1beta3/auth_config_types.go | 3 ++ api/v1beta3/zz_generated.deepcopy.go | 5 +++ controllers/auth_config_controller.go | 9 ++++- .../authorino.kuadrant.io_authconfigs.yaml | 17 ++++++++++ install/manifests.yaml | 17 ++++++++++ .../authorization/kubernetes_authz.go | 34 ++++++++++++++----- .../authorization/kubernetes_authz_test.go | 17 ++++++---- 7 files changed, 87 insertions(+), 15 deletions(-) diff --git a/api/v1beta3/auth_config_types.go b/api/v1beta3/auth_config_types.go index ec162ffa..b6274d13 100644 --- a/api/v1beta3/auth_config_types.go +++ b/api/v1beta3/auth_config_types.go @@ -622,6 +622,9 @@ type KubernetesSubjectAccessReviewAuthorizationSpec struct { // Groups the user must be a member of or, if `user` is omitted, the groups to check for authorization in the Kubernetes RBAC. Groups []string `json:"groups,omitempty"` + // AuthorizationGroups is a value or selector to use as groups to check for authorization in the Kubernetes RBAC. + AuthorizationGroups *ValueOrSelector `json:"authorizationGroups,omitempty"` + // Use resourceAttributes to check permissions on Kubernetes resources. // If omitted, it performs a non-resource SubjectAccessReview, with verb and path inferred from the request. // +optional diff --git a/api/v1beta3/zz_generated.deepcopy.go b/api/v1beta3/zz_generated.deepcopy.go index cbc7f560..382a3ad7 100644 --- a/api/v1beta3/zz_generated.deepcopy.go +++ b/api/v1beta3/zz_generated.deepcopy.go @@ -779,6 +779,11 @@ func (in *KubernetesSubjectAccessReviewAuthorizationSpec) DeepCopyInto(out *Kube *out = make([]string, len(*in)) copy(*out, *in) } + if in.AuthorizationGroups != nil { + in, out := &in.AuthorizationGroups, &out.AuthorizationGroups + *out = new(ValueOrSelector) + (*in).DeepCopyInto(*out) + } if in.ResourceAttributes != nil { in, out := &in.ResourceAttributes, &out.ResourceAttributes *out = new(KubernetesSubjectAccessReviewResourceAttributesSpec) diff --git a/controllers/auth_config_controller.go b/controllers/auth_config_controller.go index 5dbce879..e54fdac0 100644 --- a/controllers/auth_config_controller.go +++ b/controllers/auth_config_controller.go @@ -503,7 +503,14 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf } } - translatedAuthorization.KubernetesAuthz, err = authorization_evaluators.NewKubernetesAuthz(authorinoUser, authorization.KubernetesSubjectAccessReview.Groups, authorinoResourceAttributes) + var authorinoGroups expressions.Value + if authorization.KubernetesSubjectAccessReview.AuthorizationGroups != nil { + authorinoGroups, err = valueFrom(authorization.KubernetesSubjectAccessReview.AuthorizationGroups) + if err != nil { + return nil, err + } + } + translatedAuthorization.KubernetesAuthz, err = authorization_evaluators.NewKubernetesAuthz(authorinoUser, authorization.KubernetesSubjectAccessReview.Groups, authorinoGroups, authorinoResourceAttributes) if err != nil { return nil, err } diff --git a/install/crd/authorino.kuadrant.io_authconfigs.yaml b/install/crd/authorino.kuadrant.io_authconfigs.yaml index 242ba008..df0b3ac7 100644 --- a/install/crd/authorino.kuadrant.io_authconfigs.yaml +++ b/install/crd/authorino.kuadrant.io_authconfigs.yaml @@ -2796,6 +2796,23 @@ spec: kubernetesSubjectAccessReview: description: Authorization by Kubernetes SubjectAccessReview properties: + authorizationGroups: + description: AuthorizationGroups is a value or selector + to use as groups to check for authorization in the Kubernetes + RBAC. + properties: + expression: + type: string + selector: + description: |- + Simple path selector to fetch content from the authorization JSON (e.g. 'request.method') or a string template with variables that resolve to patterns (e.g. "Hello, {auth.identity.name}!"). + Any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson can be used. + The following Authorino custom modifiers are supported: @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, @case:upper|lower, @base64:encode|decode and @strip. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object groups: description: Groups the user must be a member of or, if `user` is omitted, the groups to check for authorization diff --git a/install/manifests.yaml b/install/manifests.yaml index 46761613..7e0e7807 100644 --- a/install/manifests.yaml +++ b/install/manifests.yaml @@ -3104,6 +3104,23 @@ spec: kubernetesSubjectAccessReview: description: Authorization by Kubernetes SubjectAccessReview properties: + authorizationGroups: + description: AuthorizationGroups is a value or selector + to use as groups to check for authorization in the Kubernetes + RBAC. + properties: + expression: + type: string + selector: + description: |- + Simple path selector to fetch content from the authorization JSON (e.g. 'request.method') or a string template with variables that resolve to patterns (e.g. "Hello, {auth.identity.name}!"). + Any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson can be used. + The following Authorino custom modifiers are supported: @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, @case:upper|lower, @base64:encode|decode and @strip. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object groups: description: Groups the user must be a member of or, if `user` is omitted, the groups to check for authorization diff --git a/pkg/evaluators/authorization/kubernetes_authz.go b/pkg/evaluators/authorization/kubernetes_authz.go index de398938..72d81922 100644 --- a/pkg/evaluators/authorization/kubernetes_authz.go +++ b/pkg/evaluators/authorization/kubernetes_authz.go @@ -2,6 +2,7 @@ package authorization import ( gocontext "context" + gojson "encoding/json" "fmt" "strings" @@ -22,7 +23,7 @@ type kubernetesSubjectAccessReviewer interface { SubjectAccessReviews() kubeAuthzClient.SubjectAccessReviewInterface } -func NewKubernetesAuthz(user expressions.Value, groups []string, resourceAttributes *KubernetesAuthzResourceAttributes) (*KubernetesAuthz, error) { +func NewKubernetesAuthz(user expressions.Value, groups []string, authorizationGroups expressions.Value, resourceAttributes *KubernetesAuthzResourceAttributes) (*KubernetesAuthz, error) { config, err := rest.InClusterConfig() if err != nil { return nil, err @@ -34,10 +35,11 @@ func NewKubernetesAuthz(user expressions.Value, groups []string, resourceAttribu } return &KubernetesAuthz{ - User: user, - Groups: groups, - ResourceAttributes: resourceAttributes, - authorizer: k8sClient.AuthorizationV1(), + User: user, + Groups: groups, + AuthorizationGroups: authorizationGroups, + ResourceAttributes: resourceAttributes, + authorizer: k8sClient.AuthorizationV1(), }, nil } @@ -51,9 +53,10 @@ type KubernetesAuthzResourceAttributes struct { } type KubernetesAuthz struct { - User expressions.Value - Groups []string - ResourceAttributes *KubernetesAuthzResourceAttributes + User expressions.Value + Groups []string + AuthorizationGroups expressions.Value + ResourceAttributes *KubernetesAuthzResourceAttributes authorizer kubernetesSubjectAccessReviewer } @@ -131,6 +134,21 @@ func (k *KubernetesAuthz) Call(pipeline auth.AuthPipeline, ctx gocontext.Context if len(k.Groups) > 0 { subjectAccessReview.Spec.Groups = k.Groups + } else if k.AuthorizationGroups != nil { + resolvedValue, err := k.AuthorizationGroups.ResolveFor(authJSON) + if err != nil { + return nil, err + } + stringJson, err := json.StringifyJSON(resolvedValue) + if err != nil { + return nil, err + } + var resolvedGroups []string + err = gojson.Unmarshal([]byte(stringJson), &resolvedGroups) + if err != nil { + return nil, err + } + subjectAccessReview.Spec.Groups = resolvedGroups } log.FromContext(ctx).WithName("kubernetesauthz").V(1).Info("calling kubernetes subject access review api", "subjectaccessreview", subjectAccessReview) diff --git a/pkg/evaluators/authorization/kubernetes_authz_test.go b/pkg/evaluators/authorization/kubernetes_authz_test.go index 77a54826..fa18f81b 100644 --- a/pkg/evaluators/authorization/kubernetes_authz_test.go +++ b/pkg/evaluators/authorization/kubernetes_authz_test.go @@ -59,11 +59,12 @@ func (client *k8sAuthorizationClientMock) GetRequest() kubeAuthz.SubjectAccessRe return client.request } -func newKubernetesAuthz(user expressions.Value, groups []string, resourceAttributes *KubernetesAuthzResourceAttributes, subjectAccessReviewResponseStatus kubeAuthz.SubjectAccessReviewStatus) *KubernetesAuthz { +func newKubernetesAuthz(user expressions.Value, groups []string, authorizationGroups expressions.Value, resourceAttributes *KubernetesAuthzResourceAttributes, subjectAccessReviewResponseStatus kubeAuthz.SubjectAccessReviewStatus) *KubernetesAuthz { return &KubernetesAuthz{ - User: user, - Groups: groups, - ResourceAttributes: resourceAttributes, + User: user, + Groups: groups, + AuthorizationGroups: authorizationGroups, + ResourceAttributes: resourceAttributes, // mock the authorizer so we can control the response authorizer: &k8sAuthorizationClientMock{SubjectAccessReviewStatus: subjectAccessReviewResponseStatus}, @@ -75,7 +76,7 @@ func TestKubernetesAuthzNonResource_Allowed(t *testing.T) { defer ctrl.Finish() pipelineMock := mock_auth.NewMockAuthPipeline(ctrl) - pipelineMock.EXPECT().GetAuthorizationJSON().Return(`{"context":{"request":{"http":{"method":"GET","path":"/hello"}}},"auth":{"identity":{"username":"john"}}}`) + pipelineMock.EXPECT().GetAuthorizationJSON().Return(`{"context":{"request":{"http":{"method":"GET","path":"/hello"}}},"auth":{"identity":{"username":"john", "groups":["group1","group2"]}}}`) request := &envoy_auth.AttributeContext_HttpRequest{Method: "GET", Path: "/hello"} pipelineMock.EXPECT().GetHttp().Return(request) @@ -83,6 +84,7 @@ func TestKubernetesAuthzNonResource_Allowed(t *testing.T) { kubernetesAuth := newKubernetesAuthz( &json.JSONValue{Pattern: "auth.identity.username"}, []string{}, + &json.JSONValue{Pattern: "auth.identity.groups"}, nil, kubeAuthz.SubjectAccessReviewStatus{Allowed: true, Reason: ""}, ) @@ -112,6 +114,7 @@ func TestKubernetesAuthzNonResource_Denied(t *testing.T) { &json.JSONValue{Pattern: "auth.identity.username"}, []string{}, nil, + nil, kubeAuthz.SubjectAccessReviewStatus{Allowed: false, Reason: "some-reason"}, ) authorized, err := kubernetesAuth.Call(pipelineMock, context.TODO()) @@ -131,11 +134,12 @@ func TestKubernetesAuthzResource_Allowed(t *testing.T) { defer ctrl.Finish() pipelineMock := mock_auth.NewMockAuthPipeline(ctrl) - pipelineMock.EXPECT().GetAuthorizationJSON().Return(`{"context":{"request":{"http":{"method":"GET","path":"/hello"}}},"auth":{"identity":{"username":"john"}}}`) + pipelineMock.EXPECT().GetAuthorizationJSON().Return(`{"context":{"request":{"http":{"method":"GET","path":"/hello"}}},"auth":{"identity":{"username":"john", "groups":["group1","group2"]}}}`) kubernetesAuth := newKubernetesAuthz( &json.JSONValue{Pattern: "auth.identity.username"}, []string{}, + &json.JSONValue{Pattern: "auth.identity.groups"}, &KubernetesAuthzResourceAttributes{Namespace: &json.JSONValue{Static: "default"}}, kubeAuthz.SubjectAccessReviewStatus{Allowed: true, Reason: ""}, ) @@ -160,6 +164,7 @@ func TestKubernetesAuthzResource_Denied(t *testing.T) { kubernetesAuth := newKubernetesAuthz( &json.JSONValue{Pattern: "auth.identity.username"}, []string{}, + nil, &KubernetesAuthzResourceAttributes{Namespace: &json.JSONValue{Static: "default"}}, kubeAuthz.SubjectAccessReviewStatus{Allowed: false, Reason: "some-reason"}, ) From 0a97b99434be712354f1f3ca1466ec0e5efa256a Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Mon, 18 Nov 2024 12:30:18 -0800 Subject: [PATCH 2/5] Update AuthorizationGroups description. Co-authored-by: Guilherme Cassolato Signed-off-by: Dhiraj Bokde --- api/v1beta3/auth_config_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1beta3/auth_config_types.go b/api/v1beta3/auth_config_types.go index b6274d13..2f27acf8 100644 --- a/api/v1beta3/auth_config_types.go +++ b/api/v1beta3/auth_config_types.go @@ -622,7 +622,7 @@ type KubernetesSubjectAccessReviewAuthorizationSpec struct { // Groups the user must be a member of or, if `user` is omitted, the groups to check for authorization in the Kubernetes RBAC. Groups []string `json:"groups,omitempty"` - // AuthorizationGroups is a value or selector to use as groups to check for authorization in the Kubernetes RBAC. + // Groups to check for existing permission in the Kubernetes RBAC alternatively to a specific user. This is typically obtained from a list of groups the user is a member of. Must be a static list of group names or dynamically resolve to one from the Authorization JSON. AuthorizationGroups *ValueOrSelector `json:"authorizationGroups,omitempty"` // Use resourceAttributes to check permissions on Kubernetes resources. From a9018225d1e5a6086eb86924044606033063f7fc Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Mon, 18 Nov 2024 12:47:14 -0800 Subject: [PATCH 3/5] fix: code cleanup, added checks in tests to verify groups are passed in SAR API call Signed-off-by: Dhiraj Bokde --- controllers/auth_config_controller.go | 6 ++- .../authorization/kubernetes_authz.go | 14 ++----- .../authorization/kubernetes_authz_test.go | 40 +++++-------------- 3 files changed, 18 insertions(+), 42 deletions(-) diff --git a/controllers/auth_config_controller.go b/controllers/auth_config_controller.go index e54fdac0..d1c7d5ea 100644 --- a/controllers/auth_config_controller.go +++ b/controllers/auth_config_controller.go @@ -504,13 +504,17 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf } var authorinoGroups expressions.Value + // look for authorizationGroups first if authorization.KubernetesSubjectAccessReview.AuthorizationGroups != nil { authorinoGroups, err = valueFrom(authorization.KubernetesSubjectAccessReview.AuthorizationGroups) if err != nil { return nil, err } + } else if len(authorization.KubernetesSubjectAccessReview.Groups) > 0 { + // use deprecated Groups property otherwise + authorinoGroups = &json.JSONValue{Static: authorization.KubernetesSubjectAccessReview.Groups} } - translatedAuthorization.KubernetesAuthz, err = authorization_evaluators.NewKubernetesAuthz(authorinoUser, authorization.KubernetesSubjectAccessReview.Groups, authorinoGroups, authorinoResourceAttributes) + translatedAuthorization.KubernetesAuthz, err = authorization_evaluators.NewKubernetesAuthz(authorinoUser, authorinoGroups, authorinoResourceAttributes) if err != nil { return nil, err } diff --git a/pkg/evaluators/authorization/kubernetes_authz.go b/pkg/evaluators/authorization/kubernetes_authz.go index 72d81922..357f4d8e 100644 --- a/pkg/evaluators/authorization/kubernetes_authz.go +++ b/pkg/evaluators/authorization/kubernetes_authz.go @@ -23,7 +23,7 @@ type kubernetesSubjectAccessReviewer interface { SubjectAccessReviews() kubeAuthzClient.SubjectAccessReviewInterface } -func NewKubernetesAuthz(user expressions.Value, groups []string, authorizationGroups expressions.Value, resourceAttributes *KubernetesAuthzResourceAttributes) (*KubernetesAuthz, error) { +func NewKubernetesAuthz(user expressions.Value, authorizationGroups expressions.Value, resourceAttributes *KubernetesAuthzResourceAttributes) (*KubernetesAuthz, error) { config, err := rest.InClusterConfig() if err != nil { return nil, err @@ -36,7 +36,6 @@ func NewKubernetesAuthz(user expressions.Value, groups []string, authorizationGr return &KubernetesAuthz{ User: user, - Groups: groups, AuthorizationGroups: authorizationGroups, ResourceAttributes: resourceAttributes, authorizer: k8sClient.AuthorizationV1(), @@ -54,7 +53,6 @@ type KubernetesAuthzResourceAttributes struct { type KubernetesAuthz struct { User expressions.Value - Groups []string AuthorizationGroups expressions.Value ResourceAttributes *KubernetesAuthzResourceAttributes @@ -132,14 +130,8 @@ func (k *KubernetesAuthz) Call(pipeline auth.AuthPipeline, ctx gocontext.Context } } - if len(k.Groups) > 0 { - subjectAccessReview.Spec.Groups = k.Groups - } else if k.AuthorizationGroups != nil { - resolvedValue, err := k.AuthorizationGroups.ResolveFor(authJSON) - if err != nil { - return nil, err - } - stringJson, err := json.StringifyJSON(resolvedValue) + if k.AuthorizationGroups != nil { + stringJson, err := jsonValueToStr(k.AuthorizationGroups) if err != nil { return nil, err } diff --git a/pkg/evaluators/authorization/kubernetes_authz_test.go b/pkg/evaluators/authorization/kubernetes_authz_test.go index fa18f81b..075fca22 100644 --- a/pkg/evaluators/authorization/kubernetes_authz_test.go +++ b/pkg/evaluators/authorization/kubernetes_authz_test.go @@ -11,6 +11,7 @@ import ( envoy_auth "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" "github.com/golang/mock/gomock" "gotest.tools/assert" + is "gotest.tools/assert/cmp" kubeAuthz "k8s.io/api/authorization/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kubeAuthzClient "k8s.io/client-go/kubernetes/typed/authorization/v1" @@ -59,10 +60,9 @@ func (client *k8sAuthorizationClientMock) GetRequest() kubeAuthz.SubjectAccessRe return client.request } -func newKubernetesAuthz(user expressions.Value, groups []string, authorizationGroups expressions.Value, resourceAttributes *KubernetesAuthzResourceAttributes, subjectAccessReviewResponseStatus kubeAuthz.SubjectAccessReviewStatus) *KubernetesAuthz { +func newKubernetesAuthz(user expressions.Value, authorizationGroups expressions.Value, resourceAttributes *KubernetesAuthzResourceAttributes, subjectAccessReviewResponseStatus kubeAuthz.SubjectAccessReviewStatus) *KubernetesAuthz { return &KubernetesAuthz{ User: user, - Groups: groups, AuthorizationGroups: authorizationGroups, ResourceAttributes: resourceAttributes, @@ -81,18 +81,13 @@ func TestKubernetesAuthzNonResource_Allowed(t *testing.T) { request := &envoy_auth.AttributeContext_HttpRequest{Method: "GET", Path: "/hello"} pipelineMock.EXPECT().GetHttp().Return(request) - kubernetesAuth := newKubernetesAuthz( - &json.JSONValue{Pattern: "auth.identity.username"}, - []string{}, - &json.JSONValue{Pattern: "auth.identity.groups"}, - nil, - kubeAuthz.SubjectAccessReviewStatus{Allowed: true, Reason: ""}, - ) + kubernetesAuth := newKubernetesAuthz(&json.JSONValue{Pattern: "auth.identity.username"}, &json.JSONValue{Pattern: "auth.identity.groups"}, nil, kubeAuthz.SubjectAccessReviewStatus{Allowed: true, Reason: ""}) authorized, err := kubernetesAuth.Call(pipelineMock, context.TODO()) client, _ := kubernetesAuth.authorizer.(subjectAccessReviewTestClient) requestData := client.GetRequest() assert.Equal(t, requestData.User, "john") + assert.DeepEqual(t, requestData.Groups, []string{"group1", "group2"}) assert.Equal(t, requestData.NonResourceAttributes.Path, "/hello") assert.Equal(t, requestData.NonResourceAttributes.Verb, "get") @@ -110,18 +105,13 @@ func TestKubernetesAuthzNonResource_Denied(t *testing.T) { request := &envoy_auth.AttributeContext_HttpRequest{Method: "GET", Path: "/hello"} pipelineMock.EXPECT().GetHttp().Return(request) - kubernetesAuth := newKubernetesAuthz( - &json.JSONValue{Pattern: "auth.identity.username"}, - []string{}, - nil, - nil, - kubeAuthz.SubjectAccessReviewStatus{Allowed: false, Reason: "some-reason"}, - ) + kubernetesAuth := newKubernetesAuthz(&json.JSONValue{Pattern: "auth.identity.username"}, nil, nil, kubeAuthz.SubjectAccessReviewStatus{Allowed: false, Reason: "some-reason"}) authorized, err := kubernetesAuth.Call(pipelineMock, context.TODO()) client, _ := kubernetesAuth.authorizer.(subjectAccessReviewTestClient) requestData := client.GetRequest() assert.Equal(t, requestData.User, "john") + assert.Assert(t, is.Len(requestData.Groups, 0)) assert.Equal(t, requestData.NonResourceAttributes.Path, "/hello") assert.Equal(t, requestData.NonResourceAttributes.Verb, "get") @@ -136,13 +126,7 @@ func TestKubernetesAuthzResource_Allowed(t *testing.T) { pipelineMock := mock_auth.NewMockAuthPipeline(ctrl) pipelineMock.EXPECT().GetAuthorizationJSON().Return(`{"context":{"request":{"http":{"method":"GET","path":"/hello"}}},"auth":{"identity":{"username":"john", "groups":["group1","group2"]}}}`) - kubernetesAuth := newKubernetesAuthz( - &json.JSONValue{Pattern: "auth.identity.username"}, - []string{}, - &json.JSONValue{Pattern: "auth.identity.groups"}, - &KubernetesAuthzResourceAttributes{Namespace: &json.JSONValue{Static: "default"}}, - kubeAuthz.SubjectAccessReviewStatus{Allowed: true, Reason: ""}, - ) + kubernetesAuth := newKubernetesAuthz(&json.JSONValue{Pattern: "auth.identity.username"}, &json.JSONValue{Pattern: "auth.identity.groups"}, &KubernetesAuthzResourceAttributes{Namespace: &json.JSONValue{Static: "default"}}, kubeAuthz.SubjectAccessReviewStatus{Allowed: true, Reason: ""}) authorized, err := kubernetesAuth.Call(pipelineMock, context.TODO()) assert.Check(t, authorized.(bool)) @@ -151,6 +135,7 @@ func TestKubernetesAuthzResource_Allowed(t *testing.T) { client, _ := kubernetesAuth.authorizer.(subjectAccessReviewTestClient) requestData := client.GetRequest() assert.Equal(t, requestData.User, "john") + assert.DeepEqual(t, requestData.Groups, []string{"group1", "group2"}) assert.Equal(t, requestData.ResourceAttributes.Namespace, "default") } @@ -161,13 +146,7 @@ func TestKubernetesAuthzResource_Denied(t *testing.T) { pipelineMock := mock_auth.NewMockAuthPipeline(ctrl) pipelineMock.EXPECT().GetAuthorizationJSON().Return(`{"context":{"request":{"http":{"method":"GET","path":"/hello"}}},"auth":{"identity":{"username":"john"}}}`) - kubernetesAuth := newKubernetesAuthz( - &json.JSONValue{Pattern: "auth.identity.username"}, - []string{}, - nil, - &KubernetesAuthzResourceAttributes{Namespace: &json.JSONValue{Static: "default"}}, - kubeAuthz.SubjectAccessReviewStatus{Allowed: false, Reason: "some-reason"}, - ) + kubernetesAuth := newKubernetesAuthz(&json.JSONValue{Pattern: "auth.identity.username"}, &json.JSONValue{Static: []string{"group1", "group2"}}, &KubernetesAuthzResourceAttributes{Namespace: &json.JSONValue{Static: "default"}}, kubeAuthz.SubjectAccessReviewStatus{Allowed: false, Reason: "some-reason"}) authorized, err := kubernetesAuth.Call(pipelineMock, context.TODO()) assert.Check(t, !authorized.(bool)) @@ -176,5 +155,6 @@ func TestKubernetesAuthzResource_Denied(t *testing.T) { client, _ := kubernetesAuth.authorizer.(subjectAccessReviewTestClient) requestData := client.GetRequest() assert.Equal(t, requestData.User, "john") + assert.DeepEqual(t, requestData.Groups, []string{"group1", "group2"}) assert.Equal(t, requestData.ResourceAttributes.Namespace, "default") } From b707476bf04f3a898356cab57086a05a0860aa41 Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Tue, 19 Nov 2024 08:16:52 -0800 Subject: [PATCH 4/5] fix: documented k8s SAR authz groups property as deprecated Signed-off-by: Dhiraj Bokde --- api/v1beta3/auth_config_types.go | 1 + install/crd/authorino.kuadrant.io_authconfigs.yaml | 14 ++++++++------ install/manifests.yaml | 14 ++++++++------ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/api/v1beta3/auth_config_types.go b/api/v1beta3/auth_config_types.go index 2f27acf8..4abe3857 100644 --- a/api/v1beta3/auth_config_types.go +++ b/api/v1beta3/auth_config_types.go @@ -620,6 +620,7 @@ type KubernetesSubjectAccessReviewAuthorizationSpec struct { User *ValueOrSelector `json:"user,omitempty"` // Groups the user must be a member of or, if `user` is omitted, the groups to check for authorization in the Kubernetes RBAC. + // Deprecated: Use authorizationGroups instead. Groups []string `json:"groups,omitempty"` // Groups to check for existing permission in the Kubernetes RBAC alternatively to a specific user. This is typically obtained from a list of groups the user is a member of. Must be a static list of group names or dynamically resolve to one from the Authorization JSON. diff --git a/install/crd/authorino.kuadrant.io_authconfigs.yaml b/install/crd/authorino.kuadrant.io_authconfigs.yaml index df0b3ac7..be3a33bb 100644 --- a/install/crd/authorino.kuadrant.io_authconfigs.yaml +++ b/install/crd/authorino.kuadrant.io_authconfigs.yaml @@ -2797,9 +2797,11 @@ spec: description: Authorization by Kubernetes SubjectAccessReview properties: authorizationGroups: - description: AuthorizationGroups is a value or selector - to use as groups to check for authorization in the Kubernetes - RBAC. + description: Groups to check for existing permission in + the Kubernetes RBAC alternatively to a specific user. + This is typically obtained from a list of groups the user + is a member of. Must be a static list of group names or + dynamically resolve to one from the Authorization JSON. properties: expression: type: string @@ -2814,9 +2816,9 @@ spec: x-kubernetes-preserve-unknown-fields: true type: object groups: - description: Groups the user must be a member of or, if - `user` is omitted, the groups to check for authorization - in the Kubernetes RBAC. + description: |- + Groups the user must be a member of or, if `user` is omitted, the groups to check for authorization in the Kubernetes RBAC. + Deprecated: Use authorizationGroups instead. items: type: string type: array diff --git a/install/manifests.yaml b/install/manifests.yaml index 7e0e7807..85db028c 100644 --- a/install/manifests.yaml +++ b/install/manifests.yaml @@ -3105,9 +3105,11 @@ spec: description: Authorization by Kubernetes SubjectAccessReview properties: authorizationGroups: - description: AuthorizationGroups is a value or selector - to use as groups to check for authorization in the Kubernetes - RBAC. + description: Groups to check for existing permission in + the Kubernetes RBAC alternatively to a specific user. + This is typically obtained from a list of groups the user + is a member of. Must be a static list of group names or + dynamically resolve to one from the Authorization JSON. properties: expression: type: string @@ -3122,9 +3124,9 @@ spec: x-kubernetes-preserve-unknown-fields: true type: object groups: - description: Groups the user must be a member of or, if - `user` is omitted, the groups to check for authorization - in the Kubernetes RBAC. + description: |- + Groups the user must be a member of or, if `user` is omitted, the groups to check for authorization in the Kubernetes RBAC. + Deprecated: Use authorizationGroups instead. items: type: string type: array From 1353e296bac530cef5e3d257ce0b4e0478c55f18 Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Wed, 20 Nov 2024 09:18:33 -0800 Subject: [PATCH 5/5] fix: updated manifests Signed-off-by: Dhiraj Bokde --- install/crd/authorino.kuadrant.io_authconfigs.yaml | 3 +++ install/manifests.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/install/crd/authorino.kuadrant.io_authconfigs.yaml b/install/crd/authorino.kuadrant.io_authconfigs.yaml index be3a33bb..4f963ca4 100644 --- a/install/crd/authorino.kuadrant.io_authconfigs.yaml +++ b/install/crd/authorino.kuadrant.io_authconfigs.yaml @@ -2804,6 +2804,9 @@ spec: dynamically resolve to one from the Authorization JSON. properties: expression: + description: |- + A Common Expression Language (CEL) expression that evaluates to a value. + String expressions are supported (https://pkg.go.dev/github.com/google/cel-go/ext#Strings). type: string selector: description: |- diff --git a/install/manifests.yaml b/install/manifests.yaml index 85db028c..cd566bda 100644 --- a/install/manifests.yaml +++ b/install/manifests.yaml @@ -3112,6 +3112,9 @@ spec: dynamically resolve to one from the Authorization JSON. properties: expression: + description: |- + A Common Expression Language (CEL) expression that evaluates to a value. + String expressions are supported (https://pkg.go.dev/github.com/google/cel-go/ext#Strings). type: string selector: description: |-