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

(camel-kamelets) branch main updated: Fix #3014: support all four Salesforce authentication types (#3059)

This is an automated email from the ASF dual-hosted git repository.

oscerd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-kamelets.git


The following commit(s) were added to refs/heads/main by this push:
     new dd073ecd0 Fix #3014: support all four Salesforce authentication types (#3059)
dd073ecd0 is described below

commit dd073ecd034a27ddd8146dfb5ef1a4504e1a4060
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Thu Sep 24 11:05:46 2026 +0200

    Fix #3014: support all four Salesforce authentication types (#3059)
    
    * Fix #3014: let the Salesforce Kamelets use an authentication type other than username and password
    
    All six Salesforce Kamelets authenticated with username and password only, while
    camel-salesforce offers four authentication types. This adds the three that need
    nothing but scalar properties.
    
    The obstacle was not the new properties but the old ones: userName and password
    were listed under required in every one of the six, and CLIENT_CREDENTIALS uses
    neither, nor does REFRESH_TOKEN. There is no conditional required in the JSON
    schema these definitions use, so both move out of required and their placeholders
    become optional. Everything else is additive:
    
      authenticationType  USERNAME_PASSWORD, CLIENT_CREDENTIALS or REFRESH_TOKEN
      refreshToken        for REFRESH_TOKEN, marked as a credential
      instanceUrl         when the authentication response carries none
    
    USERNAME_PASSWORD stays the default, so an existing binding behaves exactly as
    before and keeps working unchanged.
    
    JWT is deliberately absent from the enum. It needs keystore, typed
    KeyStoreParameters rather than a scalar, and how to express that is still open on
    the issue. Listing JWT now would advertise a type that cannot authenticate.
    
    This does relax the contract: a binding that omits a username is no longer
    rejected, and the schema no longer states that username and password go together.
    That is the price of supporting more than one authentication type here. The
    reviewer checklist asks for an upgrade-guide entry on a relaxation; this
    repository has no upgrade guide, so the note belongs in the Camel guide rather
    than here.
    
    Verified by driving a message into salesforce-create-sink with
    authenticationType=CLIENT_CREDENTIALS and no userName or password. Against main
    the same driver fails with
    
      Route template salesforce-create-sink the following mandatory parameters must
      be provided: userName, password
    
    and against this branch there is no missing parameter error at all; the run only
    stops later, on an unrelated protobuf class in the local JBang environment. Build
    is green with the validator over 262 Kamelets and KameletsCatalogTest passing.
    
    Authentication against a real Salesforce org is not covered: there are no Citrus
    tests for these Kamelets and CI has no credentials.
    
    Co-Authored-By: Claude Opus 5 <no...@anthropic.com>
    
    * Fix #3014: add the JWT authentication type, taking the keystore as a bean reference
    
    Completes the issue. The previous commit left JWT out because keystore is typed
    KeyStoreParameters rather than a scalar, and how to express that was open.
    
    The catalog already answered it. opensearch-search-source, from #3000, takes
    sslContextParameters as an optional string holding a "#bean:name" reference and
    passes it straight through, and no Kamelet in the catalog builds a
    KeyStoreParameters or SSLContextParameters bean from scalars today. Following
    that precedent rather than inventing a second idiom for the same family of JSSE
    objects:
    
      keystore      "#bean:myKeystore", a registry bean of KeyStoreParameters
      jwtAudience   audience claim, usually the login URL of the target org
    
    JWT joins the enum now that it can actually authenticate.
    
    The alternative was building the KeyStoreParameters bean inside each template
    from path, password and type. It reads better for the operator, needing no bean
    registration, but Kamelet beans are constructed unconditionally, so every
    username and password user would get an empty keystore built as well. Since the
    catalog has no precedent for that and does have one for the reference, the
    reference wins.
    
    Not verified: a JWT authentication against a real org, and the binding of the
    keystore reference at runtime. The local probe stops earlier, on a protobuf class
    missing from the JBang environment, before component property binding is reached.
    What is checked here is that all six declare and wire the properties consistently,
    the validator passes over 262 Kamelets and KameletsCatalogTest is green.
    
    Co-Authored-By: Claude Opus 5 <no...@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 5 <no...@anthropic.com>
---
 .../salesforce-composite-upsert-sink.kamelet.yaml  | 42 +++++++++++++++++++---
 kamelets/salesforce-create-sink.kamelet.yaml       | 42 +++++++++++++++++++---
 kamelets/salesforce-delete-sink.kamelet.yaml       | 42 +++++++++++++++++++---
 kamelets/salesforce-pubsub-source.kamelet.yaml     | 42 +++++++++++++++++++---
 kamelets/salesforce-source.kamelet.yaml            | 42 +++++++++++++++++++---
 kamelets/salesforce-update-sink.kamelet.yaml       | 42 +++++++++++++++++++---
 6 files changed, 228 insertions(+), 24 deletions(-)

diff --git a/kamelets/salesforce-composite-upsert-sink.kamelet.yaml b/kamelets/salesforce-composite-upsert-sink.kamelet.yaml
index 0f1754b29..8e2cd2460 100644
--- a/kamelets/salesforce-composite-upsert-sink.kamelet.yaml
+++ b/kamelets/salesforce-composite-upsert-sink.kamelet.yaml
@@ -37,8 +37,6 @@ spec:
       - sObjectIdName
       - clientId
       - clientSecret
-      - userName
-      - password
     type: object
     properties:
       sObjectName:       
@@ -81,6 +79,37 @@ spec:
         format: password
         x-descriptors:
         - urn:camel:group:credentials
+      authenticationType:
+        title: Authentication Type
+        description: Authentication type to use. USERNAME_PASSWORD needs userName and password, CLIENT_CREDENTIALS needs only the consumer key and secret, REFRESH_TOKEN needs refreshToken, JWT needs keystore and jwtAudience.
+        type: string
+        default: USERNAME_PASSWORD
+        enum: ["USERNAME_PASSWORD", "CLIENT_CREDENTIALS", "REFRESH_TOKEN", "JWT"]
+      refreshToken:
+        title: Refresh Token
+        description: Refresh token used by the REFRESH_TOKEN authentication type.
+        type: string
+        format: password
+        x-descriptors:
+        - urn:camel:group:credentials
+      instanceUrl:
+        title: Instance URL
+        description: Salesforce instance URL, needed when the authentication response does not carry one.
+        type: string
+        example: "https://myinstance.my.salesforce.com"
+      jwtAudience:
+        title: JWT Audience
+        description: Audience claim for the JWT authentication type, usually the login URL of the target org.
+        type: string
+        example: "https://login.salesforce.com"
+      keystore:
+        title: Keystore
+        description: >-
+          Reference to a registry bean of type org.apache.camel.support.jsse.KeyStoreParameters,
+          written as "#bean:myBeanName", holding the certificate that signs the JWT. Required by
+          the JWT authentication type and ignored by the others.
+        type: string
+        example: "#bean:myKeystore"
   types:
     in:
       mediaType: application/json
@@ -99,9 +128,14 @@ spec:
           sObjectIdName: "{{sObjectIdName}}"
           clientId: "{{clientId}}"
           clientSecret: "{{clientSecret}}"
-          userName: "{{userName}}"
-          password: "{{password}}"
+          authenticationType: "{{authenticationType}}"
+          userName: "{{?userName}}"
+          password: "{{?password}}"
+          refreshToken: "{{?refreshToken}}"
           loginUrl: "{{loginUrl}}"
+          instanceUrl: "{{?instanceUrl}}"
+          jwtAudience: "{{?jwtAudience}}"
+          keystore: "{{?keystore}}"
     from:
       uri: kamelet:source
       steps:
diff --git a/kamelets/salesforce-create-sink.kamelet.yaml b/kamelets/salesforce-create-sink.kamelet.yaml
index ff7f5cc61..87ae92a0d 100644
--- a/kamelets/salesforce-create-sink.kamelet.yaml
+++ b/kamelets/salesforce-create-sink.kamelet.yaml
@@ -34,8 +34,6 @@ spec:
     required:
       - clientId
       - clientSecret
-      - userName
-      - password
     type: object
     properties:
       sObjectName:
@@ -74,6 +72,37 @@ spec:
         format: password
         x-descriptors:
         - urn:camel:group:credentials
+      authenticationType:
+        title: Authentication Type
+        description: Authentication type to use. USERNAME_PASSWORD needs userName and password, CLIENT_CREDENTIALS needs only the consumer key and secret, REFRESH_TOKEN needs refreshToken, JWT needs keystore and jwtAudience.
+        type: string
+        default: USERNAME_PASSWORD
+        enum: ["USERNAME_PASSWORD", "CLIENT_CREDENTIALS", "REFRESH_TOKEN", "JWT"]
+      refreshToken:
+        title: Refresh Token
+        description: Refresh token used by the REFRESH_TOKEN authentication type.
+        type: string
+        format: password
+        x-descriptors:
+        - urn:camel:group:credentials
+      instanceUrl:
+        title: Instance URL
+        description: Salesforce instance URL, needed when the authentication response does not carry one.
+        type: string
+        example: "https://myinstance.my.salesforce.com"
+      jwtAudience:
+        title: JWT Audience
+        description: Audience claim for the JWT authentication type, usually the login URL of the target org.
+        type: string
+        example: "https://login.salesforce.com"
+      keystore:
+        title: Keystore
+        description: >-
+          Reference to a registry bean of type org.apache.camel.support.jsse.KeyStoreParameters,
+          written as "#bean:myBeanName", holding the certificate that signs the JWT. Required by
+          the JWT authentication type and ignored by the others.
+        type: string
+        example: "#bean:myKeystore"
   types:
     in:
       mediaType: application/json
@@ -87,9 +116,14 @@ spec:
         properties:
           clientId: "{{clientId}}"
           clientSecret: "{{clientSecret}}"
-          userName: "{{userName}}"
-          password: "{{password}}"
+          authenticationType: "{{authenticationType}}"
+          userName: "{{?userName}}"
+          password: "{{?password}}"
+          refreshToken: "{{?refreshToken}}"
           loginUrl: "{{loginUrl}}"
+          instanceUrl: "{{?instanceUrl}}"
+          jwtAudience: "{{?jwtAudience}}"
+          keystore: "{{?keystore}}"
     from:
       uri: kamelet:source
       steps:
diff --git a/kamelets/salesforce-delete-sink.kamelet.yaml b/kamelets/salesforce-delete-sink.kamelet.yaml
index b6b9b23e1..4fb7171e5 100644
--- a/kamelets/salesforce-delete-sink.kamelet.yaml
+++ b/kamelets/salesforce-delete-sink.kamelet.yaml
@@ -34,8 +34,6 @@ spec:
     required:
       - clientId
       - clientSecret
-      - userName
-      - password
     type: object
     properties:
       loginUrl:
@@ -69,6 +67,37 @@ spec:
         format: password
         x-descriptors:
         - urn:camel:group:credentials
+      authenticationType:
+        title: Authentication Type
+        description: Authentication type to use. USERNAME_PASSWORD needs userName and password, CLIENT_CREDENTIALS needs only the consumer key and secret, REFRESH_TOKEN needs refreshToken, JWT needs keystore and jwtAudience.
+        type: string
+        default: USERNAME_PASSWORD
+        enum: ["USERNAME_PASSWORD", "CLIENT_CREDENTIALS", "REFRESH_TOKEN", "JWT"]
+      refreshToken:
+        title: Refresh Token
+        description: Refresh token used by the REFRESH_TOKEN authentication type.
+        type: string
+        format: password
+        x-descriptors:
+        - urn:camel:group:credentials
+      instanceUrl:
+        title: Instance URL
+        description: Salesforce instance URL, needed when the authentication response does not carry one.
+        type: string
+        example: "https://myinstance.my.salesforce.com"
+      jwtAudience:
+        title: JWT Audience
+        description: Audience claim for the JWT authentication type, usually the login URL of the target org.
+        type: string
+        example: "https://login.salesforce.com"
+      keystore:
+        title: Keystore
+        description: >-
+          Reference to a registry bean of type org.apache.camel.support.jsse.KeyStoreParameters,
+          written as "#bean:myBeanName", holding the certificate that signs the JWT. Required by
+          the JWT authentication type and ignored by the others.
+        type: string
+        example: "#bean:myKeystore"
   types:
     in:
       mediaType: application/json
@@ -92,9 +121,14 @@ spec:
         properties:
           clientId: "{{clientId}}"
           clientSecret: "{{clientSecret}}"
-          userName: "{{userName}}"
-          password: "{{password}}"
+          authenticationType: "{{authenticationType}}"
+          userName: "{{?userName}}"
+          password: "{{?password}}"
+          refreshToken: "{{?refreshToken}}"
           loginUrl: "{{loginUrl}}"
+          instanceUrl: "{{?instanceUrl}}"
+          jwtAudience: "{{?jwtAudience}}"
+          keystore: "{{?keystore}}"
     from:
       uri: kamelet:source
       steps:
diff --git a/kamelets/salesforce-pubsub-source.kamelet.yaml b/kamelets/salesforce-pubsub-source.kamelet.yaml
index fccd1c007..1d417ff82 100644
--- a/kamelets/salesforce-pubsub-source.kamelet.yaml
+++ b/kamelets/salesforce-pubsub-source.kamelet.yaml
@@ -35,8 +35,6 @@ spec:
       - topic
       - clientId
       - clientSecret
-      - userName
-      - password
     type: object
     properties:
       topic:
@@ -75,6 +73,37 @@ spec:
         format: password
         x-descriptors:
         - urn:camel:group:credentials
+      authenticationType:
+        title: Authentication Type
+        description: Authentication type to use. USERNAME_PASSWORD needs userName and password, CLIENT_CREDENTIALS needs only the consumer key and secret, REFRESH_TOKEN needs refreshToken, JWT needs keystore and jwtAudience.
+        type: string
+        default: USERNAME_PASSWORD
+        enum: ["USERNAME_PASSWORD", "CLIENT_CREDENTIALS", "REFRESH_TOKEN", "JWT"]
+      refreshToken:
+        title: Refresh Token
+        description: Refresh token used by the REFRESH_TOKEN authentication type.
+        type: string
+        format: password
+        x-descriptors:
+        - urn:camel:group:credentials
+      instanceUrl:
+        title: Instance URL
+        description: Salesforce instance URL, needed when the authentication response does not carry one.
+        type: string
+        example: "https://myinstance.my.salesforce.com"
+      jwtAudience:
+        title: JWT Audience
+        description: Audience claim for the JWT authentication type, usually the login URL of the target org.
+        type: string
+        example: "https://login.salesforce.com"
+      keystore:
+        title: Keystore
+        description: >-
+          Reference to a registry bean of type org.apache.camel.support.jsse.KeyStoreParameters,
+          written as "#bean:myBeanName", holding the certificate that signs the JWT. Required by
+          the JWT authentication type and ignored by the others.
+        type: string
+        example: "#bean:myKeystore"
       deserializeType:
         title: Deserialize Type
         description: >-
@@ -114,9 +143,14 @@ spec:
         properties:
           clientId: "{{clientId}}"
           clientSecret: "{{clientSecret}}"
-          userName: "{{userName}}"
-          password: "{{password}}"
+          authenticationType: "{{authenticationType}}"
+          userName: "{{?userName}}"
+          password: "{{?password}}"
+          refreshToken: "{{?refreshToken}}"
           loginUrl: "{{loginUrl}}"
+          instanceUrl: "{{?instanceUrl}}"
+          jwtAudience: "{{?jwtAudience}}"
+          keystore: "{{?keystore}}"
     from:
       uri: "{{local-salesforce-pubsub}}:pubSubSubscribe:{{topic}}"
       parameters:
diff --git a/kamelets/salesforce-source.kamelet.yaml b/kamelets/salesforce-source.kamelet.yaml
index b33677bb9..37a59b6f6 100644
--- a/kamelets/salesforce-source.kamelet.yaml
+++ b/kamelets/salesforce-source.kamelet.yaml
@@ -36,8 +36,6 @@ spec:
       - topicName
       - clientId
       - clientSecret
-      - userName
-      - password
     type: object
     properties:
       query:
@@ -87,6 +85,37 @@ spec:
         format: password
         x-descriptors:
         - urn:camel:group:credentials
+      authenticationType:
+        title: Authentication Type
+        description: Authentication type to use. USERNAME_PASSWORD needs userName and password, CLIENT_CREDENTIALS needs only the consumer key and secret, REFRESH_TOKEN needs refreshToken, JWT needs keystore and jwtAudience.
+        type: string
+        default: USERNAME_PASSWORD
+        enum: ["USERNAME_PASSWORD", "CLIENT_CREDENTIALS", "REFRESH_TOKEN", "JWT"]
+      refreshToken:
+        title: Refresh Token
+        description: Refresh token used by the REFRESH_TOKEN authentication type.
+        type: string
+        format: password
+        x-descriptors:
+        - urn:camel:group:credentials
+      instanceUrl:
+        title: Instance URL
+        description: Salesforce instance URL, needed when the authentication response does not carry one.
+        type: string
+        example: "https://myinstance.my.salesforce.com"
+      jwtAudience:
+        title: JWT Audience
+        description: Audience claim for the JWT authentication type, usually the login URL of the target org.
+        type: string
+        example: "https://login.salesforce.com"
+      keystore:
+        title: Keystore
+        description: >-
+          Reference to a registry bean of type org.apache.camel.support.jsse.KeyStoreParameters,
+          written as "#bean:myBeanName", holding the certificate that signs the JWT. Required by
+          the JWT authentication type and ignored by the others.
+        type: string
+        example: "#bean:myKeystore"
       notifyForOperationCreate:
         title: Notify Operation Create
         description: Notify for create operation.
@@ -135,9 +164,14 @@ spec:
         properties:
           clientId: "{{clientId}}"
           clientSecret: "{{clientSecret}}"
-          userName: "{{userName}}"
-          password: "{{password}}"
+          authenticationType: "{{authenticationType}}"
+          userName: "{{?userName}}"
+          password: "{{?password}}"
+          refreshToken: "{{?refreshToken}}"
           loginUrl: "{{loginUrl}}"
+          instanceUrl: "{{?instanceUrl}}"
+          jwtAudience: "{{?jwtAudience}}"
+          keystore: "{{?keystore}}"
     from:
       uri: "{{local-salesforce}}:{{operation}}:{{topicName}}"
       parameters:
diff --git a/kamelets/salesforce-update-sink.kamelet.yaml b/kamelets/salesforce-update-sink.kamelet.yaml
index 776d857ef..4e70b330c 100644
--- a/kamelets/salesforce-update-sink.kamelet.yaml
+++ b/kamelets/salesforce-update-sink.kamelet.yaml
@@ -35,8 +35,6 @@ spec:
     required:
       - clientId
       - clientSecret
-      - userName
-      - password
     type: object
     properties:
       loginUrl:
@@ -70,6 +68,37 @@ spec:
         format: password
         x-descriptors:
         - urn:camel:group:credentials
+      authenticationType:
+        title: Authentication Type
+        description: Authentication type to use. USERNAME_PASSWORD needs userName and password, CLIENT_CREDENTIALS needs only the consumer key and secret, REFRESH_TOKEN needs refreshToken, JWT needs keystore and jwtAudience.
+        type: string
+        default: USERNAME_PASSWORD
+        enum: ["USERNAME_PASSWORD", "CLIENT_CREDENTIALS", "REFRESH_TOKEN", "JWT"]
+      refreshToken:
+        title: Refresh Token
+        description: Refresh token used by the REFRESH_TOKEN authentication type.
+        type: string
+        format: password
+        x-descriptors:
+        - urn:camel:group:credentials
+      instanceUrl:
+        title: Instance URL
+        description: Salesforce instance URL, needed when the authentication response does not carry one.
+        type: string
+        example: "https://myinstance.my.salesforce.com"
+      jwtAudience:
+        title: JWT Audience
+        description: Audience claim for the JWT authentication type, usually the login URL of the target org.
+        type: string
+        example: "https://login.salesforce.com"
+      keystore:
+        title: Keystore
+        description: >-
+          Reference to a registry bean of type org.apache.camel.support.jsse.KeyStoreParameters,
+          written as "#bean:myBeanName", holding the certificate that signs the JWT. Required by
+          the JWT authentication type and ignored by the others.
+        type: string
+        example: "#bean:myKeystore"
   types:
     in:
       mediaType: application/json
@@ -86,9 +115,14 @@ spec:
         properties:
           clientId: "{{clientId}}"
           clientSecret: "{{clientSecret}}"
-          userName: "{{userName}}"
-          password: "{{password}}"
+          authenticationType: "{{authenticationType}}"
+          userName: "{{?userName}}"
+          password: "{{?password}}"
+          refreshToken: "{{?refreshToken}}"
           loginUrl: "{{loginUrl}}"
+          instanceUrl: "{{?instanceUrl}}"
+          jwtAudience: "{{?jwtAudience}}"
+          keystore: "{{?keystore}}"
     from:
       uri: kamelet:source
       steps: