You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pc...@apache.org on 2026/09/23 05:56:54 UTC

(camel-k) 02/02: Fix #6778: Improve denied Kamelet diagnostics

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 <al...@gmail.com>
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)
+}