This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new b4164ad87cc9 CAMEL-24495: camel-salesforce-maven-plugin - support JWT
and Client Credentials authentication
b4164ad87cc9 is described below
commit b4164ad87cc9e8e837dad30fe4b2425365940012
Author: Torsten Mielke <[email protected]>
AuthorDate: Mon Aug 31 20:00:15 2026 +0200
CAMEL-24495: camel-salesforce-maven-plugin - support JWT and Client
Credentials authentication
The Maven plugin previously only supported Username-Password authentication,
with the grant type hardcoded in the codegen layer. Salesforce is retiring
the
USERNAME_PASSWORD grant type in Winter '27 (February 2027), so this adds
support for the JWT and Client Credentials flows.
Adds an explicit authenticationType parameter to select the auth type, while
still auto-detecting it from whichever credentials are provided. Refactors
the
codegen login config to a no-arg constructor plus setters to support that
auto-detection, and makes userName optional since Client Credentials doesn't
need it. Adds Mojo-level validation that rejects ambiguous credential
combinations early with an actionable error message — without it, omitting
the password while providing clientSecret and userName would silently
auto-detect Client Credentials instead of failing for what was meant to be
Username-Password.
Adds SalesforceMojoValidationTest covering all credential-validation paths,
and manual integration tests for all three authentication types. Refactors
AbstractSalesforceMojoTest into a static setup-helper utility class, moving
login tests into CamelSalesforceLoginManualIT to separate test
infrastructure
from test logic. Updates the plugin README with per-auth-type requirements
and adds an upgrade-guide entry.
Co-authored-by: Claude Opus 4.6 <[email protected]>
Closes #25876
---
.../codegen/AbstractSalesforceExecution.java | 28 +++-
.../camel-salesforce-maven-plugin/README.md | 182 +++++++++++++--------
.../apache/camel/maven/AbstractSalesforceMojo.java | 32 ++--
.../camel/maven/AbstractSalesforceMojoTest.java | 103 ++++--------
...Test.java => CamelSalesforceLoginManualIT.java} | 118 +++++++------
.../camel/maven/CamelSalesforceMojoManualIT.java | 4 +-
.../camel/maven/GeneratePubSubMojoManualIT.java | 4 +-
.../camel/maven/SalesforceMojoValidationTest.java | 153 +++++++++++++++++
.../org/apache/camel/maven/SchemaMojoManualIT.java | 4 +-
.../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc | 10 ++
10 files changed, 420 insertions(+), 218 deletions(-)
diff --git
a/components/camel-salesforce/camel-salesforce-codegen/src/main/java/org/apache/camel/component/salesforce/codegen/AbstractSalesforceExecution.java
b/components/camel-salesforce/camel-salesforce-codegen/src/main/java/org/apache/camel/component/salesforce/codegen/AbstractSalesforceExecution.java
index 043e09e5a35f..4be104abe72e 100644
---
a/components/camel-salesforce/camel-salesforce-codegen/src/main/java/org/apache/camel/component/salesforce/codegen/AbstractSalesforceExecution.java
+++
b/components/camel-salesforce/camel-salesforce-codegen/src/main/java/org/apache/camel/component/salesforce/codegen/AbstractSalesforceExecution.java
@@ -25,6 +25,7 @@ import java.util.Set;
import java.util.concurrent.ExecutorService;
import org.apache.camel.CamelContext;
+import org.apache.camel.component.salesforce.AuthenticationType;
import org.apache.camel.component.salesforce.SalesforceHttpClient;
import org.apache.camel.component.salesforce.SalesforceLoginConfig;
import org.apache.camel.component.salesforce.api.SalesforceException;
@@ -157,6 +158,11 @@ public abstract class AbstractSalesforceExecution {
*/
String userName;
+ /**
+ * Salesforce authentication type.
+ */
+ AuthenticationType authenticationType;
+
/**
* Salesforce API version.
*/
@@ -321,14 +327,16 @@ public abstract class AbstractSalesforceExecution {
}
private SalesforceLoginConfig getSalesforceLoginSession() {
- if (keyStoreParameters != null) {
- SalesforceLoginConfig salesforceLoginConfig
- = new SalesforceLoginConfig(loginUrl, clientId, userName,
keyStoreParameters, false);
- salesforceLoginConfig.setJwtAudience(jwtAudience);
-
- return salesforceLoginConfig;
- }
- return new SalesforceLoginConfig(loginUrl, clientId, clientSecret,
userName, password, false);
+ SalesforceLoginConfig config = new SalesforceLoginConfig();
+ config.setLoginUrl(loginUrl);
+ config.setClientId(clientId);
+ config.setClientSecret(clientSecret);
+ config.setUserName(userName);
+ config.setPassword(password);
+ config.setKeystore(keyStoreParameters);
+ config.setJwtAudience(jwtAudience);
+ config.setType(authenticationType);
+ return config;
}
private void disconnectFromSalesforce(final RestClient restClient) {
@@ -416,6 +424,10 @@ public abstract class AbstractSalesforceExecution {
this.keyStoreParameters = keyStoreParameters;
}
+ public void setAuthenticationType(AuthenticationType authenticationType) {
+ this.authenticationType = authenticationType;
+ }
+
public void setUserName(String userName) {
this.userName = userName;
}
diff --git
a/components/camel-salesforce/camel-salesforce-maven-plugin/README.md
b/components/camel-salesforce/camel-salesforce-maven-plugin/README.md
index d06a2c1ea8a5..131cd7f431f2 100644
--- a/components/camel-salesforce/camel-salesforce-maven-plugin/README.md
+++ b/components/camel-salesforce/camel-salesforce-maven-plugin/README.md
@@ -4,7 +4,7 @@ This plugin generates DTOs for use with the [Camel Salesforce
Component](https:/
## Usage ##
-This plugin provides three maven goals:
+This plugin provides three Maven goals:
* The `generate` goal generates DTOs for use with the REST API.
* The `generatePubSub` goal generates Apache Avro `SpecificRecord` subclasses
for use with the PubSub API.
@@ -12,29 +12,30 @@ This plugin provides three maven goals:
The plugin configuration has the following properties.
-* clientId - Salesforce client Id for Remote API access
-* clientSecret - Salesforce client secret for Remote API access
-* userName - Salesforce account username
-* password - Salesforce account password (including secret token)
-* jwtAudience - Salesforce JWT audience (defaults to
"https://login.salesforce.com")
-* keystoreResource - Path to keystore file for JWT authentication
-* keystorePassword - Password for keystore file
-* keystoreType - Type of keystore file (defaults to "JKS")
-* loginUrl - Salesforce loginUrl (defaults to "https://login.salesforce.com")
-* version - Salesforce Rest API version, defaults to 25.0
-* outputDirectory - Directory where to place generated DTOs, defaults to
${project.build.directory}/generated-sources/camel-salesforce
-* includes - List of SObject types to include
-* topics - List of topics to include, .e.g., `/event/BatchApexErrorEvent`.
This property only applies to the `generatePubSub` goal.
-* excludes - List of SObject types to exclude
-* includePattern - Java RegEx for SObject types to include
-* excludePattern - Java RegEx for SObject types to exclude
-* packageName - Java package name for generated DTOs, defaults to
org.apache.camel.salesforce.dto.
-* customTypes - override default types in generated DTOs
-* useStringsForPicklists - Use strings instead of enumerations for picklists.
Default is false.
-* childRelationshipNameSuffix - Suffix for child relationship property name.
Necessary if an SObject
+* `clientId` - Salesforce client Id for Remote API access
+* `clientSecret` - Salesforce client secret for Remote API access
+* `userName` - Salesforce account username (required for Username-Password and
JWT flows)
+* `password` - Salesforce account password (including secret token)
+* `authenticationType` - Salesforce authentication type: USERNAME_PASSWORD,
JWT, or CLIENT_CREDENTIALS. If not specified, auto-detected from provided
credentials.
+* `jwtAudience` - Salesforce JWT audience (defaults to
"https://login.salesforce.com")
+* `keystoreResource` - Path to keystore file for JWT authentication
+* `keystorePassword` - Password for keystore file
+* `keystoreType` - Type of keystore file (defaults to "JKS")
+* `loginUrl` - Salesforce loginUrl (defaults to "https://login.salesforce.com")
+* `version` - Salesforce Rest API version, defaults to 25.0
+* `outputDirectory` - Directory where to place generated DTOs, defaults to
${project.build.directory}/generated-sources/camel-salesforce
+* `includes` - List of SObject types to include
+* `topics` - List of topics to include, e.g., `/event/BatchApexErrorEvent`.
This property only applies to the `generatePubSub` goal.
+* `excludes` - List of SObject types to exclude
+* `includePattern` - Java RegEx for SObject types to include
+* `excludePattern` - Java RegEx for SObject types to exclude
+* `packageName` - Java package name for generated DTOs, defaults to
org.apache.camel.salesforce.dto.
+* `customTypes` - override default types in generated DTOs
+* `useStringsForPicklists` - Use strings instead of enumerations for
picklists. Default is false.
+* `childRelationshipNameSuffix` - Suffix for child relationship property name.
Necessary if an SObject
has a lookup field with the same name as its Child Relationship Name. If
setting to something other
than default, "List" is a sensible value.
-* enumerationOverrideProperties - Override picklist enum value generation via
a java.util.Properties instance.
+* `enumerationOverrideProperties` - Override picklist enum value generation
via a java.util.Properties instance.
Property name format: `SObject.FieldName.PicklistValue`. Property value is the
desired enum value. E.g.:
```
<enumerationOverrideProperties>
@@ -47,57 +48,63 @@ Property name format: `SObject.FieldName.PicklistValue`.
Property value is the d
Additional properties to provide proxy information, if behind a firewall.
-* httpProxyHost
-* httpProxyPort
-* httpProxyUsername
-* httpProxyPassword
-* httpProxyRealm
-* httpProxyAuthUri
-* httpProxyUseDigestAuth
-* httpProxyIncludedAddresses
-* httpProxyExcludedAddresses
+* `httpProxyHost`
+* `httpProxyPort`
+* `httpProxyUsername`
+* `httpProxyPassword`
+* `httpProxyRealm`
+* `httpProxyAuthUri`
+* `httpProxyUseDigestAuth`
+* `httpProxyIncludedAddresses`
+* `httpProxyExcludedAddresses`
-There are two authentication methods supported by the plugin:
Username-Password and JWT.
-The plugin will use the Username-Password method if the `clientSecret` is
specified and will use the JWT method if the `keystoreResource` is specified.
+Three authentication methods are supported by the plugin: Username-Password,
JWT, and Client Credentials.
+The plugin auto-detects the authentication method from the provided
credentials, or you can set `authenticationType` explicitly.
-Sample pom.xml using Username-Password authentication:
+* **Username-Password** requires: `clientId`, `clientSecret`, `userName`, and
`password`.<br>
+ Auto-detected when `password` is specified.
+* **JWT** requires: `clientId`, `userName`, `loginUrl` (My Domain URL),
`keystoreResource`, and `keystorePassword`.<br>
+ `keystoreType` defaults to JKS, `jwtAudience` defaults to
`https://login.salesforce.com`.<br>
+ Auto-detected when `keystoreResource` is specified.
+* **Client Credentials** requires: `clientId`, `clientSecret`, and `loginUrl`
(My Domain URL).<br>
+ Auto-detected when only `clientId` and `clientSecret` are specified (no
`password`, no `userName`, no `keystoreResource`).
+
+___
+<br>
+
+### Username-Password Authentication Type ###
+
+Sample pom.xml using **Username-Password** authentication:
```
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<properties>
-
-
<camelSalesforce.clientId>5MVG9uudbyLbNPZOFutIHJpIb2nchnCiNE_NqeYcewMCPPT8_6VV_LQF_CJ813456GxzhxZdxlGwbYI_yzHmz</camelSalesforce.clientId>
+
<camelSalesforce.clientId>5MVG9uudbyLbNPZOFut...GwbYI_yzHmz</camelSalesforce.clientId>
<camelSalesforce.clientSecret>5630289243049151316</camelSalesforce.clientSecret>
<camelSalesforce.userName>[email protected]</camelSalesforce.userName>
<camelSalesforce.password>foopasswordCbe5V27JxD0JXYFGJIdIEWB7p</camelSalesforce.password>
-
-
<camelSalesforce.loginUrl>https://myDomain.my.salesforce.com</camelSalesforce.loginUrl>
-
+
<camelSalesforce.loginUrl>https://myDomain.my.salesforce.com</camelSalesforce.loginUrl>
<camelSalesforce.httpProxyHost>foo.bar.com</camelSalesforce.httpProxyHost>
<camelSalesforce.httpProxyPort>8090</camelSalesforce.httpProxyPort>
-
</properties>
-
...
-
<build>
...
<plugins>
...
-
- <!-- camel maven saleforce for creating salesforce
objects -->
+ <!-- camel maven salesforce for creating salesforce
objects -->
<plugin>
<groupId>org.apache.camel.maven</groupId>
<artifactId>camel-salesforce-maven-plugin</artifactId>
- <version>2.17.1</version>
+ <version>${camel.version}</version>
<configuration>
<clientId>${camelSalesforce.clientId}</clientId>
<clientSecret>${camelSalesforce.clientSecret}</clientSecret>
<userName>${camelSalesforce.userName}</userName>
<password>${camelSalesforce.password}</password>
- <loginUrl
default-value="https://login.salesforce.com">${camelSalesforce.loginUrl}</loginUrl>
+
<loginUrl>${camelSalesforce.loginUrl}</loginUrl>
<includes>
<include>Account</include>
<include>Contacts</include>
@@ -106,7 +113,6 @@ Sample pom.xml using Username-Password authentication:
<httpProxyPort>${camelSalesforce.httpProxyPort}</httpProxyPort>
</configuration>
</plugin>
-
</plugins>
</build>
@@ -117,49 +123,46 @@ The plugin should be configured for the rest of the
properties, and can be execu
mvn camel-salesforce:generate -DcamelSalesforce.clientId=<clientid>
-DcamelSalesforce.clientSecret=<clientsecret>
-DcamelSalesforce.userName=<username> -DcamelSalesforce.password=<password>
-Sample pom.xml using JWT authentication:
+___
+<br>
+
+### JWT Authentication Type ###
+
+Sample pom.xml using **JWT** authentication:
```
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<properties>
-
-
<camelSalesforce.clientId>5MVG9uudbyLbNPZOFutIHJpIb2nchnCiNE_NqeYcewMCPPT8_6VV_LQF_CJ813456GxzhxZdxlGwbYI_yzHmz</camelSalesforce.clientId>
+
<camelSalesforce.clientId>5MVG9uudbyLbNPZOFut...GwbYI_yzHmz</camelSalesforce.clientId>
<camelSalesforce.userName>[email protected]</camelSalesforce.userName>
<camelSalesforce.keystore.resource>src/main/resources/salesforce.jks</camelSalesforce.keystore.resource>
<camelSalesforce.keystore.password>foopasswordCbe5V27JxD0JXYFGJIdIEWB7p</camelSalesforce.keystore.password>
<camelSalesforce.keystore.type>JKS</camelSalesforce.keystore.type>
-
<camelSalesforce.jwtAudience>https://login.salesforce.com</camelSalesforce.jwtAudience>
-
-
<camelSalesforce.loginUrl>https://myDomain.my.salesforce.com</camelSalesforce.loginUrl>
-
+
<camelSalesforce.loginUrl>https://myDomain.my.salesforce.com</camelSalesforce.loginUrl>
<camelSalesforce.httpProxyHost>foo.bar.com</camelSalesforce.httpProxyHost>
<camelSalesforce.httpProxyPort>8090</camelSalesforce.httpProxyPort>
-
</properties>
-
...
-
<build>
...
<plugins>
...
-
- <!-- camel maven saleforce for creating salesforce
objects -->
+ <!-- camel maven salesforce for creating salesforce
objects -->
<plugin>
<groupId>org.apache.camel.maven</groupId>
<artifactId>camel-salesforce-maven-plugin</artifactId>
- <version>2.17.1</version>
+ <version>${camel.version}</version>
<configuration>
<clientId>${camelSalesforce.clientId}</clientId>
<userName>${camelSalesforce.userName}</userName>
<keystoreResource>${camelSalesforce.keystore.resource}</keystoreResource>
<keystorePassword>${camelSalesforce.keystore.password}</keystorePassword>
- <keystoreType
default-value="JKS">${camelSalesforce.keystore.type}</keystoreType>
- <jwtAudience
default-value="https://login.salesforce.com">${camelSalesforce.jwtAudience}</jwtAudience>
- <loginUrl
default-value="https://login.salesforce.com">${camelSalesforce.loginUrl}</loginUrl>
+
<keystoreType>${camelSalesforce.keystore.type}</keystoreType>
+
<jwtAudience>${camelSalesforce.jwtAudience}</jwtAudience>
+
<loginUrl>${camelSalesforce.loginUrl}</loginUrl>
<includes>
<include>Account</include>
<include>Contacts</include>
@@ -168,7 +171,6 @@ Sample pom.xml using JWT authentication:
<httpProxyPort>${camelSalesforce.httpProxyPort}</httpProxyPort>
</configuration>
</plugin>
-
</plugins>
</build>
@@ -177,9 +179,57 @@ Sample pom.xml using JWT authentication:
For obvious security reasons it is recommended that the clientId, userName,
keystoreResource, keystorePassword, keystoreType and jwtAudience fields be not
set in the pom.xml.
The plugin should be configured for the rest of the properties, and can be
executed using the following command:
- mvn camel-salesforce:generate -DcamelSalesforce.clientId=<clientid>
-DcamelSalesforce.userName=<username>
-DcamelSalesforce.keystore.resource=<keystoreResource>
-DcamelSalesforce.keystore.password=<keystorePassword>
-DcamelSalesforce.keystore.type=<keystoreType>
-DcamelSalesforce.jwtAudience=<jwtAudience>
+ mvn camel-salesforce:generate -DcamelSalesforce.clientId=<clientid>
-DcamelSalesforce.userName=<username>
-DcamelSalesforce.keystore.resource=<keystoreResource>
-DcamelSalesforce.keystore.password=<keystorePassword>
-DcamelSalesforce.keystore.type=<keystoreType>
-DcamelSalesforce.jwtAudience=<jwtAudience>
-DcamelSalesforce.loginUrl=<login-url>
+
+___
+<br>
+
+### Client Credentials Authentication Type ###
+
+Sample pom.xml using **Client Credentials** authentication
+```
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <properties>
+
<camelSalesforce.clientId>5MVG9uudbyLbNPZOFut...GwbYI_yzHmz</camelSalesforce.clientId>
+
<camelSalesforce.clientSecret>5630289243049151316</camelSalesforce.clientSecret>
+
<camelSalesforce.loginUrl>https://myDomain.my.salesforce.com</camelSalesforce.loginUrl>
+ </properties>
+ ...
+ <build>
+ ...
+ <plugins>
+ ...
+ <!-- camel maven salesforce for creating salesforce
objects -->
+ <plugin>
+ <groupId>org.apache.camel.maven</groupId>
+
<artifactId>camel-salesforce-maven-plugin</artifactId>
+ <version>${camel.version}</version>
+ <configuration>
+
<clientId>${camelSalesforce.clientId}</clientId>
+
<clientSecret>${camelSalesforce.clientSecret}</clientSecret>
+
<loginUrl>${camelSalesforce.loginUrl}</loginUrl>
+ <includes>
+ <include>Account</include>
+ <include>Contacts</include>
+ </includes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
+```
+For obvious security reasons it is recommended that the clientId and
clientSecret fields be not set in the pom.xml.
+The plugin should be configured for the rest of the properties, and can be
executed using the following command:
+
+ mvn camel-salesforce:generate -DcamelSalesforce.clientId=<clientid>
-DcamelSalesforce.clientSecret=<clientsecret>
-DcamelSalesforce.loginUrl=<login-url>
+___
+
The generated DTOs use Jackson. All Salesforce field types are supported. Date
and time fields are mapped to java.time.ZonedDateTime, and picklist fields are
mapped to generated Java Enumerations.
Relationship fields, e.g. `Contact.Account`, will be strongly typed if the
referenced SObject type is listed in `includes`. Otherwise, the type of the
reference object will be `AbstractDescribedSObjectBase`. Some useful but
non-obvious SObjects to include are `RecordType`, `User`, `Group`, and `Name`.
@@ -205,4 +255,4 @@ You can customize types, i.e. use java.time.LocalDateTime
instead of the default
</customTypes>
</configuration>
</plugin>
-````
+```
diff --git
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/java/org/apache/camel/maven/AbstractSalesforceMojo.java
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/java/org/apache/camel/maven/AbstractSalesforceMojo.java
index 575ff03ca865..78a65bc147df 100644
---
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/java/org/apache/camel/maven/AbstractSalesforceMojo.java
+++
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/java/org/apache/camel/maven/AbstractSalesforceMojo.java
@@ -24,6 +24,7 @@ import java.security.KeyStore;
import java.util.Map;
import java.util.Set;
+import org.apache.camel.component.salesforce.AuthenticationType;
import org.apache.camel.component.salesforce.SalesforceEndpointConfig;
import org.apache.camel.component.salesforce.SalesforceLoginConfig;
import
org.apache.camel.component.salesforce.codegen.AbstractSalesforceExecution;
@@ -142,11 +143,18 @@ public abstract class AbstractSalesforceMojo extends
AbstractMojo {
final SSLContextParameters sslContextParameters = new
SSLContextParameters();
/**
- * Salesforce username.
+ * Salesforce username. Required for USERNAME_PASSWORD and JWT
authentication types.
*/
- @Parameter(property = "camelSalesforce.userName", required = true)
+ @Parameter(property = "camelSalesforce.userName")
String userName;
+ /**
+ * Salesforce authentication type. If not specified, auto-detected from
provided credentials. Supported values:
+ * USERNAME_PASSWORD, JWT, CLIENT_CREDENTIALS.
+ */
+ @Parameter(property = "camelSalesforce.authenticationType")
+ AuthenticationType authenticationType;
+
/**
* Salesforce JWT Audience.
*/
@@ -213,6 +221,7 @@ public abstract class AbstractSalesforceMojo extends
AbstractMojo {
execution.setLoginUrl(loginUrl);
execution.setUserName(userName);
execution.setPassword(password);
+ execution.setAuthenticationType(authenticationType);
execution.setVersion(version);
execution.setSslContextParameters(sslContextParameters);
execution.setJwtAudience(jwtAudience);
@@ -225,15 +234,7 @@ public abstract class AbstractSalesforceMojo extends
AbstractMojo {
"Either property: clientSecret or property:
keystoreResource must be provided.");
} else if (clientSecret != null && keystoreResource != null) {
throw new MojoExecutionException(
- "Property: clientSecret or property: keystoreResource must
be provided.");
- }
-
- if (clientSecret != null) {
- if (password == null) {
- throw new MojoExecutionException(
- // NOTE: a text error message to clarify the problem
- "Property 'password' must be provided when property
'clientSecret' was provided."); // NOSONAR
- }
+ "Only one of clientSecret or keystoreResource may be
provided, not both.");
}
if (keystoreResource != null) {
@@ -243,6 +244,15 @@ public abstract class AbstractSalesforceMojo extends
AbstractMojo {
"Property 'keystorePassword' must be provided when
property 'keystoreResource' was provided."); // NOSONAR
}
}
+
+ if (authenticationType == null && clientSecret != null && userName !=
null && password == null
+ && keystoreResource == null) {
+ throw new MojoExecutionException(
+ "Ambiguous authentication configuration: 'userName' and
'clientSecret' are set but 'password' is missing. "
+ + "For Username-Password
authentication, provide the 'password' property. "
+ + "For Client Credentials
authentication, remove the 'userName' property "
+ + "or set 'authenticationType' to
CLIENT_CREDENTIALS explicitly.");
+ }
}
private KeyStoreParameters generateKeyStoreParameters() {
diff --git
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
index f0f177af315a..98f2bdffb99e 100644
---
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
+++
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
@@ -20,88 +20,18 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
import java.util.Properties;
+import org.apache.camel.component.salesforce.AuthenticationType;
import org.apache.camel.component.salesforce.SalesforceEndpointConfig;
-import
org.apache.camel.component.salesforce.codegen.AbstractSalesforceExecution;
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.MojoFailureException;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
public abstract class AbstractSalesforceMojoTest {
- private static final Map<String, List<String>> NO_HEADERS =
Collections.emptyMap();
+ static final String TEST_LOGIN_PROPERTIES =
"../test-salesforce-login.properties";
- private static final String TEST_LOGIN_PROPERTIES =
"../test-salesforce-login.properties";
-
- @Test
- public void shouldLoginAndProvideRestClient() throws IOException,
MojoExecutionException, MojoFailureException {
- final AbstractSalesforceMojo mojo = new AbstractSalesforceMojo() {
- final Logger logger =
LoggerFactory.getLogger(AbstractSalesforceExecution.class.getName());
-
- @Override
- protected AbstractSalesforceExecution getSalesforceExecution() {
- return new AbstractSalesforceExecution() {
- @Override
- protected void executeWithClient() {
- assertThat(getRestClient()).isNotNull();
-
- getRestClient().getGlobalObjects(NO_HEADERS,
(response, headers, exception) -> {
- assertThat(exception).isNull();
- });
- }
-
- @Override
- protected Logger getLog() {
- return logger;
- }
- };
- }
- };
-
- setup(mojo);
-
- mojo.execute();
- }
-
- @Test
- public void shouldLoginWithJwtAndProvideRestClient() throws IOException,
MojoExecutionException, MojoFailureException {
- final AbstractSalesforceMojo mojo = new AbstractSalesforceMojo() {
- final Logger logger =
LoggerFactory.getLogger(AbstractSalesforceExecution.class.getName());
-
- @Override
- protected AbstractSalesforceExecution getSalesforceExecution() {
- return new AbstractSalesforceExecution() {
- @Override
- protected void executeWithClient() {
- assertThat(getRestClient()).isNotNull();
-
- getRestClient().getGlobalObjects(NO_HEADERS,
(response, headers, exception) -> {
- assertThat(exception).isNull();
- });
- }
-
- @Override
- protected Logger getLog() {
- return logger;
- }
- };
- }
- };
-
- setupJwt(mojo);
-
- mojo.execute();
- }
-
- static void setup(final AbstractSalesforceMojo mojo) throws IOException {
+ static void setupUsernamePassword(final AbstractSalesforceMojo mojo)
throws IOException {
// load test-salesforce-login properties
try (final InputStream stream = new
FileInputStream(TEST_LOGIN_PROPERTIES)) {
final Properties properties = new Properties();
@@ -110,6 +40,9 @@ public abstract class AbstractSalesforceMojoTest {
mojo.clientSecret =
properties.getProperty("salesforce.client.secret");
mojo.userName = properties.getProperty("salesforce.username");
mojo.password = properties.getProperty("salesforce.password");
+ assumeTrue(mojo.password != null && !mojo.password.isEmpty(),
+ "Property 'salesforce.password' must be set in " +
TEST_LOGIN_PROPERTIES
+ + "
for USERNAME_PASSWORD authentication test");
mojo.loginUrl = properties.getProperty("salesforce.login.url");
mojo.version = SalesforceEndpointConfig.DEFAULT_VERSION;
} catch (final FileNotFoundException e) {
@@ -147,4 +80,26 @@ public abstract class AbstractSalesforceMojoTest {
throw exception;
}
}
+
+ static void setupClientCredentials(final AbstractSalesforceMojo mojo)
throws IOException {
+ // load test-salesforce-login properties
+ try (final InputStream stream = new
FileInputStream(TEST_LOGIN_PROPERTIES)) {
+ final Properties properties = new Properties();
+ properties.load(stream);
+ mojo.clientId = properties.getProperty("salesforce.client.id");
+ mojo.clientSecret =
properties.getProperty("salesforce.client.secret");
+ mojo.authenticationType = AuthenticationType.CLIENT_CREDENTIALS;
+ mojo.loginUrl = properties.getProperty("salesforce.login.url");
+ mojo.version = SalesforceEndpointConfig.DEFAULT_VERSION;
+ } catch (final FileNotFoundException e) {
+ final FileNotFoundException exception
+ = new FileNotFoundException(
+ "Create a properties file named " +
TEST_LOGIN_PROPERTIES
+ + " with clientId,
clientSecret"
+ + " for a Salesforce connected
app configured for Client Credentials flow.");
+ exception.initCause(e);
+
+ throw exception;
+ }
+ }
}
diff --git
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceLoginManualIT.java
similarity index 51%
copy from
components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
copy to
components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceLoginManualIT.java
index f0f177af315a..d324b5f63dd4 100644
---
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
+++
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceLoginManualIT.java
@@ -16,16 +16,11 @@
*/
package org.apache.camel.maven;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Properties;
-import org.apache.camel.component.salesforce.SalesforceEndpointConfig;
import
org.apache.camel.component.salesforce.codegen.AbstractSalesforceExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
@@ -33,16 +28,47 @@ import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static
org.apache.camel.maven.AbstractSalesforceMojoTest.setupClientCredentials;
+import static org.apache.camel.maven.AbstractSalesforceMojoTest.setupJwt;
+import static
org.apache.camel.maven.AbstractSalesforceMojoTest.setupUsernamePassword;
import static org.assertj.core.api.Assertions.assertThat;
-public abstract class AbstractSalesforceMojoTest {
+/**
+ * Integration test that verifies Salesforce login with all supported
authentication types. The {@code ManualIT} suffix
+ * prevents automatic execution by Maven Surefire and Failsafe — run
explicitly with:
+ *
+ * <pre>
+ * mvn test -Dtest=CamelSalesforceLoginManualIT
+ * </pre>
+ *
+ * Requires a properties file at {@code ../test-salesforce-login.properties}
with:
+ *
+ * <pre>
+ * # Required for USERNAME_PASSWORD test
+ * salesforce.client.id=...
+ * salesforce.client.secret=...
+ * salesforce.username=...
+ * salesforce.password=...
+ * salesforce.login.url=https://your-domain.my.salesforce.com
+ *
+ * # Required for CLIENT_CREDENTIALS test (uses client.id, client.secret,
login.url from above)
+ *
+ * # Required for JWT test
+ * salesforce.keystore.resource=...
+ * salesforce.keystore.password=...
+ * salesforce.keystore.type=JKS
+ * </pre>
+ */
+public class CamelSalesforceLoginManualIT {
private static final Map<String, List<String>> NO_HEADERS =
Collections.emptyMap();
- private static final String TEST_LOGIN_PROPERTIES =
"../test-salesforce-login.properties";
+ private static final Logger logger =
LoggerFactory.getLogger(CamelSalesforceLoginManualIT.class.getName());
@Test
- public void shouldLoginAndProvideRestClient() throws IOException,
MojoExecutionException, MojoFailureException {
+ public void shouldLoginWithUsernamePasswordAndProvideRestClient()
+ throws IOException, MojoExecutionException, MojoFailureException {
+ logger.info("Testing
shouldLoginWithUsernamePasswordAndProvideRestClient()");
final AbstractSalesforceMojo mojo = new AbstractSalesforceMojo() {
final Logger logger =
LoggerFactory.getLogger(AbstractSalesforceExecution.class.getName());
@@ -66,13 +92,14 @@ public abstract class AbstractSalesforceMojoTest {
}
};
- setup(mojo);
+ setupUsernamePassword(mojo);
mojo.execute();
}
@Test
public void shouldLoginWithJwtAndProvideRestClient() throws IOException,
MojoExecutionException, MojoFailureException {
+ logger.info("Testing shouldLoginWithJwtAndProvideRestClient()");
final AbstractSalesforceMojo mojo = new AbstractSalesforceMojo() {
final Logger logger =
LoggerFactory.getLogger(AbstractSalesforceExecution.class.getName());
@@ -101,50 +128,35 @@ public abstract class AbstractSalesforceMojoTest {
mojo.execute();
}
- static void setup(final AbstractSalesforceMojo mojo) throws IOException {
- // load test-salesforce-login properties
- try (final InputStream stream = new
FileInputStream(TEST_LOGIN_PROPERTIES)) {
- final Properties properties = new Properties();
- properties.load(stream);
- mojo.clientId = properties.getProperty("salesforce.client.id");
- mojo.clientSecret =
properties.getProperty("salesforce.client.secret");
- mojo.userName = properties.getProperty("salesforce.username");
- mojo.password = properties.getProperty("salesforce.password");
- mojo.loginUrl = properties.getProperty("salesforce.login.url");
- mojo.version = SalesforceEndpointConfig.DEFAULT_VERSION;
- } catch (final FileNotFoundException e) {
- final FileNotFoundException exception
- = new FileNotFoundException(
- "Create a properties file named " +
TEST_LOGIN_PROPERTIES
- + " with clientId,
clientSecret, userName, password"
- + " for a Salesforce account
with Merchandise and Invoice objects from Salesforce Guides.");
- exception.initCause(e);
-
- throw exception;
- }
- }
+ @Test
+ public void shouldLoginWithClientCredentialsAndProvideRestClient()
+ throws IOException, MojoExecutionException, MojoFailureException {
+ logger.info("Testing
shouldLoginWithClientCredentialsAndProvideRestClient()");
+ final AbstractSalesforceMojo mojo = new AbstractSalesforceMojo() {
+ final Logger logger =
LoggerFactory.getLogger(AbstractSalesforceExecution.class.getName());
+
+ @Override
+ protected AbstractSalesforceExecution getSalesforceExecution() {
+ return new AbstractSalesforceExecution() {
+ @Override
+ protected void executeWithClient() {
+ assertThat(getRestClient()).isNotNull();
+
+ getRestClient().getGlobalObjects(NO_HEADERS,
(response, headers, exception) -> {
+ assertThat(exception).isNull();
+ });
+ }
+
+ @Override
+ protected Logger getLog() {
+ return logger;
+ }
+ };
+ }
+ };
- static void setupJwt(final AbstractSalesforceMojo mojo) throws IOException
{
- // load test-salesforce-login properties
- try (final InputStream stream = new
FileInputStream(TEST_LOGIN_PROPERTIES)) {
- final Properties properties = new Properties();
- properties.load(stream);
- mojo.clientId = properties.getProperty("salesforce.client.id");
- mojo.userName = properties.getProperty("salesforce.username");
- mojo.loginUrl = properties.getProperty("salesforce.login.url");
- mojo.keystoreResource =
properties.getProperty("salesforce.keystore.resource");
- mojo.keystorePassword =
properties.getProperty("salesforce.keystore.password");
- mojo.keystoreType =
properties.getProperty("salesforce.keystore.type");
- mojo.version = SalesforceEndpointConfig.DEFAULT_VERSION;
- } catch (final FileNotFoundException e) {
- final FileNotFoundException exception
- = new FileNotFoundException(
- "Create a properties file named " +
TEST_LOGIN_PROPERTIES
- + " with clientId, userName,
keyStoreResource, keyStorePassword, keyStoreType"
- + " for a Salesforce account
with Merchandise and Invoice objects from Salesforce Guides.");
- exception.initCause(e);
-
- throw exception;
- }
+ setupClientCredentials(mojo);
+
+ mojo.execute();
}
}
diff --git
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceMojoManualIT.java
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceMojoManualIT.java
index 888a52f358af..86e697820a9b 100644
---
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceMojoManualIT.java
+++
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceMojoManualIT.java
@@ -34,7 +34,7 @@ import
org.apache.camel.component.salesforce.SalesforceEndpointConfig;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import static org.apache.camel.maven.AbstractSalesforceMojoTest.setup;
+import static
org.apache.camel.maven.AbstractSalesforceMojoTest.setupUsernamePassword;
import static org.assertj.core.api.Assertions.assertThat;
public class CamelSalesforceMojoManualIT {
@@ -71,7 +71,7 @@ public class CamelSalesforceMojoManualIT {
final GenerateMojo mojo = new GenerateMojo();
// set login properties
- setup(mojo);
+ setupUsernamePassword(mojo);
// set defaults
mojo.version = SalesforceEndpointConfig.DEFAULT_VERSION;
diff --git
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/GeneratePubSubMojoManualIT.java
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/GeneratePubSubMojoManualIT.java
index 19ae4db8c6b1..f82a36137111 100644
---
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/GeneratePubSubMojoManualIT.java
+++
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/GeneratePubSubMojoManualIT.java
@@ -35,7 +35,7 @@ import com.google.testing.compile.JavaFileObjects;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import static org.apache.camel.maven.AbstractSalesforceMojoTest.setup;
+import static
org.apache.camel.maven.AbstractSalesforceMojoTest.setupUsernamePassword;
import static org.assertj.core.api.Assertions.assertThat;
public class GeneratePubSubMojoManualIT {
@@ -74,7 +74,7 @@ public class GeneratePubSubMojoManualIT {
final GeneratePubSubMojo mojo = new GeneratePubSubMojo();
// set login properties
- setup(mojo);
+ setupUsernamePassword(mojo);
// set additional properties specific to this Mojo
try (final InputStream stream = new
FileInputStream(TEST_LOGIN_PROPERTIES)) {
diff --git
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SalesforceMojoValidationTest.java
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SalesforceMojoValidationTest.java
new file mode 100644
index 000000000000..c812365d5a6d
--- /dev/null
+++
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SalesforceMojoValidationTest.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.maven;
+
+import org.apache.camel.component.salesforce.AuthenticationType;
+import
org.apache.camel.component.salesforce.codegen.AbstractSalesforceExecution;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * Unit tests for authentication parameter validation in {@link
AbstractSalesforceMojo}. These tests verify that
+ * {@code validateAuthenticationParameters()} rejects invalid credential
combinations and accepts valid ones for all
+ * supported authentication types (USERNAME_PASSWORD, JWT, CLIENT_CREDENTIALS).
+ */
+public class SalesforceMojoValidationTest {
+
+ private static final String VALIDATION_PASSED = "validation passed";
+
+ private AbstractSalesforceMojo createMojo() {
+ return new AbstractSalesforceMojo() {
+ @Override
+ protected AbstractSalesforceExecution getSalesforceExecution() {
+ throw new RuntimeException(VALIDATION_PASSED);
+ }
+ };
+ }
+
+ // --- Validation rejection tests ---
+
+ // Validation must fail when no authentication credential (clientSecret or
keystoreResource) is provided
+ @Test
+ void shouldRejectWhenNeitherClientSecretNorKeystoreProvided() {
+ AbstractSalesforceMojo mojo = createMojo();
+ mojo.clientId = "test-client-id";
+
+ assertThatThrownBy(mojo::execute)
+ .isInstanceOf(MojoExecutionException.class)
+ .hasMessageContaining("Either property: clientSecret or
property: keystoreResource must be provided");
+ }
+
+ // clientSecret and keystoreResource are mutually exclusive — providing
both must be rejected
+ @Test
+ void shouldRejectWhenBothClientSecretAndKeystoreProvided() {
+ AbstractSalesforceMojo mojo = createMojo();
+ mojo.clientId = "test-client-id";
+ mojo.clientSecret = "test-secret";
+ mojo.keystoreResource = "/some/keystore.jks";
+
+ assertThatThrownBy(mojo::execute)
+ .isInstanceOf(MojoExecutionException.class)
+ .hasMessageContaining("Only one of clientSecret or
keystoreResource may be provided, not both");
+ }
+
+ // JWT authentication requires a keystore password to unlock the keystore
+ @Test
+ void shouldRejectKeystoreWithoutPassword() {
+ AbstractSalesforceMojo mojo = createMojo();
+ mojo.clientId = "test-client-id";
+ mojo.keystoreResource = "/some/keystore.jks";
+
+ assertThatThrownBy(mojo::execute)
+ .isInstanceOf(MojoExecutionException.class)
+ .hasMessageContaining("keystorePassword' must be provided");
+ }
+
+ // When clientSecret and userName are set but password is missing, the
configuration is ambiguous:
+ // it could be USERNAME_PASSWORD (missing password) or CLIENT_CREDENTIALS
(stray userName).
+ // Validation must reject this unless authenticationType is set explicitly.
+ @Test
+ void shouldRejectAmbiguousCredentialsWithoutAuthenticationType() {
+ AbstractSalesforceMojo mojo = createMojo();
+ mojo.clientId = "test-client-id";
+ mojo.clientSecret = "test-secret";
+ mojo.userName = "[email protected]";
+
+ assertThatThrownBy(mojo::execute)
+ .isInstanceOf(MojoExecutionException.class)
+ .hasMessageContaining("Ambiguous authentication
configuration");
+ }
+
+ // --- Validation acceptance tests (one per auth method) ---
+
+ // USERNAME_PASSWORD: clientSecret + userName + password is a valid,
unambiguous combination
+ @Test
+ void shouldAcceptUsernamePasswordCredentials() {
+ AbstractSalesforceMojo mojo = createMojo();
+ mojo.clientId = "test-client-id";
+ mojo.clientSecret = "test-secret";
+ mojo.userName = "[email protected]";
+ mojo.password = "test-password";
+
+ assertThatThrownBy(mojo::execute)
+ .isInstanceOf(MojoExecutionException.class)
+ .hasMessageContaining(VALIDATION_PASSED);
+ }
+
+ // JWT: keystoreResource + keystorePassword + userName is valid — no
ambiguity since clientSecret is absent
+ @Test
+ void shouldAcceptJwtCredentials() {
+ AbstractSalesforceMojo mojo = createMojo();
+ mojo.clientId = "test-client-id";
+ mojo.keystoreResource = "/some/keystore.jks";
+ mojo.keystorePassword = "test-password";
+ mojo.userName = "[email protected]";
+
+ assertThatThrownBy(mojo::execute)
+ .isInstanceOf(MojoExecutionException.class)
+ .hasMessageContaining(VALIDATION_PASSED);
+ }
+
+ // CLIENT_CREDENTIALS (auto-detected): clientSecret without userName is
unambiguously Client Credentials
+ @Test
+ void shouldAcceptClientCredentialsWithoutUserName() {
+ AbstractSalesforceMojo mojo = createMojo();
+ mojo.clientId = "test-client-id";
+ mojo.clientSecret = "test-secret";
+
+ assertThatThrownBy(mojo::execute)
+ .isInstanceOf(MojoExecutionException.class)
+ .hasMessageContaining(VALIDATION_PASSED);
+ }
+
+ // CLIENT_CREDENTIALS (explicit): clientSecret + userName would normally
be ambiguous, but setting
+ // authenticationType explicitly resolves it — validation must accept this
+ @Test
+ void shouldAcceptExplicitClientCredentialsWithUserName() {
+ AbstractSalesforceMojo mojo = createMojo();
+ mojo.clientId = "test-client-id";
+ mojo.clientSecret = "test-secret";
+ mojo.userName = "[email protected]";
+ mojo.authenticationType = AuthenticationType.CLIENT_CREDENTIALS;
+
+ assertThatThrownBy(mojo::execute)
+ .isInstanceOf(MojoExecutionException.class)
+ .hasMessageContaining(VALIDATION_PASSED);
+ }
+}
diff --git
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SchemaMojoManualIT.java
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SchemaMojoManualIT.java
index dc8b3fecba9b..6bbcd7e40625 100644
---
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SchemaMojoManualIT.java
+++
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SchemaMojoManualIT.java
@@ -26,7 +26,7 @@ import
org.apache.camel.component.salesforce.api.utils.JsonUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import static org.apache.camel.maven.AbstractSalesforceMojoTest.setup;
+import static
org.apache.camel.maven.AbstractSalesforceMojoTest.setupUsernamePassword;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class SchemaMojoManualIT {
@@ -37,7 +37,7 @@ public class SchemaMojoManualIT {
@Test
public void testExecuteJsonSchema() throws Exception {
final SchemaMojo mojo = new SchemaMojo();
- setup(mojo);
+ setupUsernamePassword(mojo);
mojo.includes = new String[] { "Account" };
mojo.outputDirectory = temp.toFile();
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index 0f8cea4457bf..fe97f08ebbed 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -512,6 +512,16 @@ Ordinary listings are unaffected, as a listed name is
normally a single path seg
resolves back inside the polled directory remains accepted. Two configurations
can newly see files skipped: a
server that reports names navigating above the polled directory, and a
`fileName` expression (used when
`useList=false`) that navigates above it. Set `jailStartingDirectory=false` if
such a path is intended.
+=== camel-salesforce
+
+The `camel-salesforce-maven-plugin` now supports JWT and Client Credentials
authentication in addition to
+the existing Username-Password flow.
+
+A new `authenticationType` configuration property allows explicitly selecting
the authentication type.
+When not set, the plugin auto-detects the type from the provided credentials,
matching the behavior of the
+Salesforce component. The `userName` property is no longer required, as it is
not needed for the Client Credentials flow.
+
+See the plugin's `README.md` for the required properties per authentication
type.
=== camel-as2