This is an automated email from the ASF dual-hosted git repository. squakez pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit bb4a4955c6841f9928ba30286ad885aafa78075c Author: AllanAlmeida <[email protected]> AuthorDate: Tue Sep 22 11:16:12 2026 -0300 Fix #6778: Improve denied Kamelet diagnostics Generated-by: OpenAI Codex --- pkg/trait/kamelets.go | 4 +-- pkg/trait/kamelets_test.go | 66 ++++++++++++++++++++++++++++++++-- pkg/util/kubernetes/permission_test.go | 36 +++++++++++++++++++ 3 files changed, 101 insertions(+), 5 deletions(-) diff --git a/pkg/trait/kamelets.go b/pkg/trait/kamelets.go index 6b3c16133..dad678579 100644 --- a/pkg/trait/kamelets.go +++ b/pkg/trait/kamelets.go @@ -234,8 +234,8 @@ func (t *kameletsTrait) calculateNamespaces(e *Environment, defaultNamespaces .. return nil, err } if !ok { - return nil, fmt.Errorf("cross-namespace Integration reference authorization denied for the ServiceAccount %s and resources kamelets", - e.Integration.Spec.ServiceAccountName) + return nil, fmt.Errorf("cross-namespace Integration reference authorization denied for the ServiceAccount %s and Kamelet %s in namespace %s", + e.Integration.Spec.ServiceAccountName, getKameletKey(kml), ns) } } diff --git a/pkg/trait/kamelets_test.go b/pkg/trait/kamelets_test.go index 91652f4dd..d471d4ed0 100644 --- a/pkg/trait/kamelets_test.go +++ b/pkg/trait/kamelets_test.go @@ -18,11 +18,14 @@ limitations under the License. package trait import ( + "context" "encoding/json" + "errors" "testing" v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" + camelclient "github.com/apache/camel-k/v2/pkg/client" "github.com/apache/camel-k/v2/pkg/internal" "github.com/apache/camel-k/v2/pkg/util/camel" @@ -32,9 +35,21 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + kubernetesfake "k8s.io/client-go/kubernetes/fake" + authorizationclientv1 "k8s.io/client-go/kubernetes/typed/authorization/v1" + k8stesting "k8s.io/client-go/testing" "k8s.io/utils/ptr" ) +type authorizationClient struct { + camelclient.Client + authorization authorizationclientv1.AuthorizationV1Interface +} + +func (c *authorizationClient) AuthorizationV1() authorizationclientv1.AuthorizationV1Interface { + return c.authorization +} + func TestConfigurationNoKameletsUsed(t *testing.T) { trait, environment := createKameletsTestEnvironment(` - from: @@ -772,7 +787,12 @@ func TestKameletNamespaceParameter(t *testing.T) { func TestCalculateKameletNamespaces(t *testing.T) { namespaces, err := calculateNamespaces( - []string{"my-kamelet", "my-kamelet?kameletNamespace=ns1", "my-kamelet?kameletVersion=v2&kameletNamespace=ns2"}, + []string{ + "my-kamelet", + "my-kamelet?kameletNamespace=ns1", + "another-kamelet?kameletNamespace=ns1", + "my-kamelet?kameletVersion=v2&kameletNamespace=ns2", + }, ) require.NoError(t, err) assert.Len(t, namespaces, 2) @@ -780,6 +800,15 @@ func TestCalculateKameletNamespaces(t *testing.T) { assert.Contains(t, namespaces, "ns2") } +func TestCalculateKameletNamespacesInvalidReference(t *testing.T) { + trait, environment := createKameletsTestEnvironment("") + trait.List = "invalid%reference?kameletNamespace=ns1" + + namespaces, err := trait.calculateNamespaces(environment) + require.ErrorContains(t, err, "could not parse kamelet namespace") + assert.Nil(t, namespaces) +} + func TestKameletMultiNamespace(t *testing.T) { flow := ` - from: @@ -835,7 +864,7 @@ func TestKameletMultiNamespace(t *testing.T) { err = trait.Apply(environment) require.Error(t, err) assert.Equal(t, "cross-namespace Integration reference authorization denied for the ServiceAccount unauth-sa "+ - "and resources kamelets", err.Error()) + "and Kamelet extra in namespace ns1", err.Error()) // Now we should good to go environment.Integration.Namespace = "default" environment.Integration.Spec.ServiceAccountName = "cross-ns-sa" @@ -870,7 +899,38 @@ func TestKameletMultiNamespaceDeniedResource(t *testing.T) { err = trait.Apply(environment) require.Error(t, err) assert.Equal(t, "cross-namespace Integration reference authorization denied for the ServiceAccount cross-ns-sa "+ - "and resources kamelets", err.Error()) + "and Kamelet restricted-kamelet in namespace ns1", err.Error()) +} + +func TestKameletMultiNamespacePermissionCheckError(t *testing.T) { + flow := ` +- from: + uri: kamelet:timer + steps: + - to: kamelet:extra?kameletNamespace=ns1 +` + trait, environment := createKameletsTestEnvironment(flow) + environment.Ctx = context.Background() + environment.Integration.Namespace = "default" + environment.Integration.Spec.ServiceAccountName = "cross-ns-sa" + + authorizationError := errors.New("subject access review failed") + client := kubernetesfake.NewSimpleClientset() + client.PrependReactor("create", "subjectaccessreviews", func(k8stesting.Action) (bool, runtime.Object, error) { + return true, nil, authorizationError + }) + environment.Client = &authorizationClient{ + Client: environment.Client, + authorization: client.AuthorizationV1(), + } + + enabled, condition, err := trait.Configure(environment) + require.NoError(t, err) + assert.True(t, enabled) + assert.Nil(t, condition) + + err = trait.Apply(environment) + require.ErrorIs(t, err, authorizationError) } func TestKameletMultiNamespaceMissing(t *testing.T) { diff --git a/pkg/util/kubernetes/permission_test.go b/pkg/util/kubernetes/permission_test.go index 10ed4f878..1d4182584 100644 --- a/pkg/util/kubernetes/permission_test.go +++ b/pkg/util/kubernetes/permission_test.go @@ -19,10 +19,13 @@ package kubernetes import ( "context" + "errors" "testing" authorizationv1 "k8s.io/api/authorization/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/kubernetes/fake" k8stesting "k8s.io/client-go/testing" @@ -61,3 +64,36 @@ func TestCheckServiceAccountPermissionByResourceName(t *testing.T) { require.NoError(t, err) require.False(t, allowed) } + +func TestCheckServiceAccountPermissionForbidden(t *testing.T) { + client := fake.NewSimpleClientset() + client.PrependReactor("create", "subjectaccessreviews", func(k8stesting.Action) (bool, runtime.Object, error) { + return true, nil, apierrors.NewForbidden( + schema.GroupResource{Group: "authorization.k8s.io", Resource: "subjectaccessreviews"}, + "", + errors.New("forbidden"), + ) + }) + + allowed, err := CheckServiceAccountPermission( + context.Background(), client, "system:serviceaccount:source:integration", + "camel.apache.org", "kamelets", "target", "denied-kamelet", "get", + ) + require.NoError(t, err) + require.False(t, allowed) +} + +func TestCheckServiceAccountPermissionError(t *testing.T) { + expected := errors.New("subject access review failed") + client := fake.NewSimpleClientset() + client.PrependReactor("create", "subjectaccessreviews", func(k8stesting.Action) (bool, runtime.Object, error) { + return true, nil, expected + }) + + allowed, err := CheckServiceAccountPermission( + context.Background(), client, "system:serviceaccount:source:integration", + "camel.apache.org", "kamelets", "target", "denied-kamelet", "get", + ) + require.ErrorIs(t, err, expected) + require.False(t, allowed) +}
