Repository: camel Updated Branches: refs/heads/master 803e37dd9 -> c63a9fd23
CAMEL-10938 Camel Salesforce : add an option to... ...retrieve login information for testing purpose from env var or system properties This introduces `authenticationType` Salesforce component property that can be used to force, as opposed auto-determine authentication flow the component should perform. It ties into the integration tests setup as it enables tests to be run with any supported authentication type. More to the point of the initial JIRA, this change enables configuring the integration tests using environment variables, Java system properties or the established `test-salesforce-login.properties` file. The syntax in the `test-salesforce-login.properties` has changed slightly to better accomodate the environment variable syntax: camelcase was dropped in favor of dot notation. Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/c63a9fd2 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/c63a9fd2 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/c63a9fd2 Branch: refs/heads/master Commit: c63a9fd235120c2b5b845e0583428b9c149a0769 Parents: f7d30c8 Author: Zoran Regvart <zregv...@apache.org> Authored: Fri Mar 24 15:58:01 2017 +0100 Committer: Zoran Regvart <zregv...@apache.org> Committed: Fri Mar 24 15:58:12 2017 +0100 ---------------------------------------------------------------------- .../camel-salesforce-component/pom.xml | 90 ++++++++++- .../src/main/docs/salesforce-component.adoc | 3 +- .../salesforce/AuthenticationType.java | 21 +++ .../salesforce/SalesforceComponent.java | 11 ++ .../salesforce/SalesforceComponentVerifier.java | 7 +- .../salesforce/SalesforceLoginConfig.java | 78 +++++---- .../salesforce/internal/SalesforceSession.java | 4 +- .../salesforce/AnalyticsApiIntegrationTest.java | 24 +-- .../component/salesforce/LoginConfigHelper.java | 162 ++++++++++++------- ...sforceJwtBearerTokenFlowIntegrationTest.java | 23 ++- components/camel-salesforce/pom.xml | 113 +++---------- .../test-salesforce-login.properties.sample | 43 +++-- .../SalesforceComponentConfiguration.java | 23 +++ 13 files changed, 371 insertions(+), 231 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/components/camel-salesforce/camel-salesforce-component/pom.xml ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/pom.xml b/components/camel-salesforce/camel-salesforce-component/pom.xml index b920db9..a68b689 100644 --- a/components/camel-salesforce/camel-salesforce-component/pom.xml +++ b/components/camel-salesforce/camel-salesforce-component/pom.xml @@ -196,6 +196,68 @@ <build> <plugins> <plugin> + <artifactId>maven-enforcer-plugin</artifactId> + <executions> + <execution> + <id>salesforce-integration-test-prerequisites</id> + <goals> + <goal>enforce</goal> + </goals> + <configuration> + <rules> + <requireFilesExist> + <files> + <file>${salesforce.component.root}/it/resources/migration-tool/ant-salesforce.jar</file> + </files> + <message><![CDATA[Salesforce Migration Tool required + +You need to download the Salesforce Migration Tool (ZIP file) and +extract the `ant-salesforce.jar` out of it to: + +${salesforce.component.root}/it/resources/migration-tool/ant-salesforce.jar + +It's needed by the integration tests to setup the Salesforce instance +with custom sObjects/fields that are required by the tests. + +Have a look at: + +${salesforce.component.root}/it/resources/salesforce + +To see what will be done. + +For information and download of the Salesforce Migration Tool consult: + +https://developer.salesforce.com/page/Force.com_Migration_Tool]]></message> + </requireFilesExist> + </rules> + <fail>true</fail> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <version>3.0.2</version> + <executions> + <execution> + <id>copy-test-salesforce-login-properties</id> + <phase>generate-test-resources</phase> + <goals> + <goal>copy-resources</goal> + </goals> + <configuration> + <outputDirectory>${project.build.directory}/test-classes/</outputDirectory> + <resources> + <resource> + <directory>${salesforce.component.root}</directory> + <include>test-salesforce-login.properties</include> + </resource> + </resources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <executions> @@ -206,21 +268,43 @@ </goals> <phase>pre-integration-test</phase> <configuration> - <target> + <target xmlns:if="ant:if" xmlns:unless="ant:unless"> <taskdef resource="com/salesforce/antlib.xml" uri="antlib:com.salesforce"> <classpath> <pathelement location="${salesforce.component.root}/it/resources/migration-tool/ant-salesforce.jar" /> </classpath> </taskdef> - <property file="${salesforce.component.root}/test-salesforce-login.properties" /> + <property prefix="prop" file="${project.build.directory}/test-classes/test-salesforce-login.properties" /> + <property environment="env" /> + + <property name="username" value="${prop.salesforce.username}" if:set="prop.salesforce.username" /> + <property name="username" value="${env.SALESFORCE_USERNAME}" if:set="env.SALESFORCE_USERNAME" /> + + <property name="password" value="${prop.salesforce.password}" if:set="prop.salesforce.password" /> + <property name="password" value="${env.SALESFORCE_PASSWORD}" if:set="env.SALESFORCE_PASSWORD" /> + + <fail unless:set="username" message="To run the migrations you need to specify either `salesforce.username` +in: ${project.build.directory}/generated-resources/test-salesforce-login.properties +or set SALESFORCE_USERNAME environment variable" /> + + <fail unless:set="password" message="To run the migrations you need to specify either `salesforce.password` +in: ${project.build.directory}/generated-resources/test-salesforce-login.properties +or set SALESFORCE_PASSWORD environment variable" /> - <sf:deploy xmlns:sf="antlib:com.salesforce" username="${userName}" password="${password}" + <sf:deploy xmlns:sf="antlib:com.salesforce" username="${username}" password="${password}" deployRoot="${salesforce.component.root}/it/resources/salesforce" rollbackOnError="true" /> </target> </configuration> </execution> </executions> + <dependencies> + <dependency> + <groupId>org.apache.ant</groupId> + <artifactId>ant</artifactId> + <version>1.10.1</version> + </dependency> + </dependencies> </plugin> <plugin> <artifactId>maven-failsafe-plugin</artifactId> http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc b/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc index 5fd49d5..d475a52 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc +++ b/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc @@ -427,13 +427,14 @@ link:salesforce.html[Salesforce]. // component options: START -The Salesforce component supports 25 options which are listed below. +The Salesforce component supports 26 options which are listed below. [width="100%",cols="2,5,^1,2",options="header"] |======================================================================= | Name | Description | Default | Type +| **authenticationType** (security) | Explicit authentication type to be used one of USERNAME_PASSWORD REFRESH_TOKEN or JWT. | | AuthenticationType | **loginConfig** (security) | To use the shared SalesforceLoginConfig as login configuration | | SalesforceLoginConfig | **loginUrl** (security) | Salesforce login URL defaults to https://login.salesforce.com | https://login.salesforce.com | String | **clientId** (security) | Salesforce connected application Consumer Key | | String http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/AuthenticationType.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/AuthenticationType.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/AuthenticationType.java new file mode 100644 index 0000000..bffc67b --- /dev/null +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/AuthenticationType.java @@ -0,0 +1,21 @@ +/** + * 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.component.salesforce; + +public enum AuthenticationType { + USERNAME_PASSWORD, REFRESH_TOKEN, JWT +} http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java index 694a55b..d80685d 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java @@ -91,6 +91,9 @@ public class SalesforceComponent extends UriEndpointComponent implements Endpoin private String password; @Metadata(label = "security", secret = true) private KeyStoreParameters keystore; + @Metadata(description="Explicit authentication type to be used, one of USERNAME_PASSWORD, REFRESH_TOKEN or JWT.", + label = "security", secret = false, enums="USERNAME_PASSWORD,REFRESH_TOKEN,JWT") + private AuthenticationType authenticationType; @Metadata(label = "security") private boolean lazyLogin; @@ -434,6 +437,14 @@ public class SalesforceComponent extends UriEndpointComponent implements Endpoin return result; } + public AuthenticationType getAuthenticationType() { + return authenticationType; + } + + public void setAuthenticationType(AuthenticationType authenticationType) { + this.authenticationType = authenticationType; + } + public SalesforceLoginConfig getLoginConfig() { return loginConfig; } http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java index 28c0a8f..c7aa082 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java @@ -23,7 +23,6 @@ import java.util.Optional; import org.apache.camel.ComponentVerifier; import org.apache.camel.NoSuchOptionException; -import org.apache.camel.component.salesforce.SalesforceLoginConfig.Type; import org.apache.camel.component.salesforce.api.SalesforceException; import org.apache.camel.component.salesforce.api.dto.RestError; import org.apache.camel.component.salesforce.internal.SalesforceSession; @@ -65,9 +64,9 @@ public class SalesforceComponentVerifier extends DefaultComponentVerifier { ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.PARAMETERS) .errors(ResultErrorHelper.requiresAny( parameters, - OptionsGroup.withName(Type.USERNAME_PASSWORD).options("clientId", "clientSecret", "userName", "password"), - OptionsGroup.withName(Type.REFRESH_TOKEN).options("clientId", "clientSecret", "refreshToken"), - OptionsGroup.withName(Type.JWT).options("clientId", "userName", "keystore") + OptionsGroup.withName(AuthenticationType.USERNAME_PASSWORD).options("clientId", "clientSecret", "userName", "password"), + OptionsGroup.withName(AuthenticationType.REFRESH_TOKEN).options("clientId", "clientSecret", "refreshToken"), + OptionsGroup.withName(AuthenticationType.JWT).options("clientId", "userName", "keystore") ) ); http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceLoginConfig.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceLoginConfig.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceLoginConfig.java index 30c0a99..cc1faab 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceLoginConfig.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceLoginConfig.java @@ -24,13 +24,9 @@ import org.apache.camel.util.jsse.KeyStoreParameters; */ public class SalesforceLoginConfig { - public enum Type { - USERNAME_PASSWORD, REFRESH_TOKEN, JWT - } - public static final String DEFAULT_LOGIN_URL = "https://login.salesforce.com"; - private Type type; + private AuthenticationType type; private String loginUrl; private String clientId; private String clientSecret; @@ -48,7 +44,7 @@ public class SalesforceLoginConfig { lazyLogin = false; } - private SalesforceLoginConfig(Type type, String loginUrl, String clientId, String clientSecret, String refreshToken, + private SalesforceLoginConfig(AuthenticationType type, String loginUrl, String clientId, String clientSecret, String refreshToken, String userName, String password, boolean lazyLogin, KeyStoreParameters keystore) { this.type = type; this.loginUrl = loginUrl; @@ -63,16 +59,16 @@ public class SalesforceLoginConfig { public SalesforceLoginConfig(String loginUrl, String clientId, String clientSecret, String userName, String password, boolean lazyLogin) { - this(Type.USERNAME_PASSWORD, loginUrl, clientId, clientSecret, null, userName, password, lazyLogin, null); + this(AuthenticationType.USERNAME_PASSWORD, loginUrl, clientId, clientSecret, null, userName, password, lazyLogin, null); } public SalesforceLoginConfig(String loginUrl, String clientId, String clientSecret, String refreshToken, boolean lazyLogin) { - this(Type.REFRESH_TOKEN, loginUrl, clientId, clientSecret, refreshToken, null, null, lazyLogin, null); + this(AuthenticationType.REFRESH_TOKEN, loginUrl, clientId, clientSecret, refreshToken, null, null, lazyLogin, null); } public SalesforceLoginConfig(String loginUrl, String clientId, String userName, KeyStoreParameters keystore, boolean lazyLogin) { - this(Type.JWT, loginUrl, clientId, null, null, userName, null, lazyLogin, keystore); + this(AuthenticationType.JWT, loginUrl, clientId, null, null, userName, null, lazyLogin, keystore); } public String getLoginUrl() { @@ -114,7 +110,6 @@ public class SalesforceLoginConfig { */ public void setKeystore(final KeyStoreParameters keystore) { this.keystore = keystore; - this.type = Type.JWT; } public KeyStoreParameters getKeystore() { @@ -130,11 +125,46 @@ public class SalesforceLoginConfig { */ public void setRefreshToken(String refreshToken) { this.refreshToken = refreshToken; - this.type = Type.REFRESH_TOKEN; } - public Type getType() { - return type; + public AuthenticationType getType() { + if (type != null) { + // use the user provided type + return type; + } + + final boolean hasPassword = ObjectHelper.isNotEmpty(password); + final boolean hasRefreshToken = ObjectHelper.isNotEmpty(refreshToken); + final boolean hasKeystore = keystore != null && ObjectHelper.isNotEmpty(keystore.getResource()); + + if (hasPassword && !hasRefreshToken && !hasKeystore) { + return AuthenticationType.USERNAME_PASSWORD; + } + + if (!hasPassword && hasRefreshToken && !hasKeystore) { + return AuthenticationType.REFRESH_TOKEN; + } + + if (!hasPassword && hasRefreshToken && hasKeystore) { + return AuthenticationType.JWT; + } + + if (hasPassword && hasRefreshToken || hasPassword && hasKeystore || hasRefreshToken && hasKeystore) { + throw new IllegalArgumentException("The provided authentication configuration can be used in multiple ways" + + " for instance both with username/password and refresh_token. Either remove some of the configuration" + + " options, so that authentication method can be auto-determined or explicitly set the authentication" + + " type."); + } + + throw new IllegalArgumentException( + "You must specify parameters aligned with one of the supported authentication methods:" + + " for username and password authentication: userName, password, clientSecret;" + + " for refresh token authentication: refreshToken, clientSecret;" + + " for JWT: userName, keystore. And for every one of those loginUrl and clientId must be specified also."); + } + + public void setType(AuthenticationType type) { + this.type = type; } public String getUserName() { @@ -157,7 +187,6 @@ public class SalesforceLoginConfig { */ public void setPassword(String password) { this.password = password; - this.type = Type.USERNAME_PASSWORD; } public boolean isLazyLogin() { @@ -176,27 +205,22 @@ public class SalesforceLoginConfig { ObjectHelper.notNull(loginUrl, "loginUrl"); ObjectHelper.notNull(clientId, "clientId"); - final boolean hasRefreshToken = ObjectHelper.isNotEmpty(refreshToken); + final AuthenticationType type = getType(); - if (!hasRefreshToken && keystore == null) { + switch (type) { + case USERNAME_PASSWORD: ObjectHelper.notNull(userName, "userName (username/password authentication)"); ObjectHelper.notNull(password, "password (username/password authentication)"); ObjectHelper.notNull(clientSecret, "clientSecret (username/password authentication)"); - type = Type.USERNAME_PASSWORD; - } else if (hasRefreshToken && keystore == null) { + break; + case REFRESH_TOKEN: ObjectHelper.notNull(refreshToken, "refreshToken (authentication with refresh token)"); ObjectHelper.notNull(clientSecret, "clientSecret (authentication with refresh token)"); - type = Type.REFRESH_TOKEN; - } else if (keystore != null) { + break; + case JWT: ObjectHelper.notNull(userName, "userName (JWT authentication)"); ObjectHelper.notNull(keystore, "keystore (JWT authentication)"); - type = Type.JWT; - } else { - throw new IllegalArgumentException( - "You must specify parameters aligned with one of the supported authentication methods:" - + " for username and password authentication: userName, password, clientSecret;" - + " for refresh token authentication: refreshToken, clientSecret;" - + " for JWT: userName, keystore. And for every one of those loginUrl and clientId must be specified also."); + break; } } http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/SalesforceSession.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/SalesforceSession.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/SalesforceSession.java index 190de11..a38749b 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/SalesforceSession.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/SalesforceSession.java @@ -38,9 +38,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.camel.CamelContext; import org.apache.camel.Service; +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.SalesforceLoginConfig.Type; import org.apache.camel.component.salesforce.api.SalesforceException; import org.apache.camel.component.salesforce.api.dto.RestError; import org.apache.camel.component.salesforce.api.utils.JsonUtils; @@ -154,7 +154,7 @@ public class SalesforceSession implements Service { fields.put("client_id", config.getClientId()); fields.put("format", "json"); - final Type type = config.getType(); + final AuthenticationType type = config.getType(); switch (type) { case USERNAME_PASSWORD: fields.put("client_secret", config.getClientSecret()); http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/AnalyticsApiIntegrationTest.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/AnalyticsApiIntegrationTest.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/AnalyticsApiIntegrationTest.java index 8df7b07..b6a16f3 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/AnalyticsApiIntegrationTest.java +++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/AnalyticsApiIntegrationTest.java @@ -63,7 +63,7 @@ public class AnalyticsApiIntegrationTest extends AbstractSalesforceTestBase { private static final int NUM_OPTIONS = REPORT_OPTIONS.length; private static final int[] POWERS = new int[] {4, 2, 1}; - private static String[] testReportNames; + private static String TEST_REPORT_NAME = "Test_Report"; private boolean bodyMetadata; /** @@ -73,27 +73,7 @@ public class AnalyticsApiIntegrationTest extends AbstractSalesforceTestBase { */ @DataPoints public static String[] getTestReportDeveloperNames() throws Exception { - return testReportNames; - } - - @BeforeClass - public static void getReportNames() throws Exception { - // get test report names - Properties testProperties = new Properties(); - testProperties.load(new FileInputStream(LoginConfigHelper.TEST_LOGIN_PROPERTIES)); - - Map<String, String> reports = new TreeMap<String, String>(); - for (Map.Entry<Object, Object> entry : testProperties.entrySet()) { - final String key = entry.getKey().toString(); - if (key.matches("report.[0-9]+")) { - reports.put(key, entry.getValue().toString()); - } - } - assertFalse("Missing entries report.[0-9]+=<Report DeveloperName> in " - + LoginConfigHelper.TEST_LOGIN_PROPERTIES, reports.isEmpty()); - - final Collection<String> reportNames = reports.values(); - testReportNames = reportNames.toArray(new String[reportNames.size()]); + return new String[] { TEST_REPORT_NAME }; } @Test http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/LoginConfigHelper.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/LoginConfigHelper.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/LoginConfigHelper.java index 987f7e1..9520271 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/LoginConfigHelper.java +++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/LoginConfigHelper.java @@ -16,81 +16,131 @@ */ package org.apache.camel.component.salesforce; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; +import java.util.HashMap; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.jsse.KeyStoreParameters; -import org.junit.Assert; -public class LoginConfigHelper extends Assert { +import static org.apache.camel.util.ObjectHelper.isNotEmpty; - protected static final String TEST_LOGIN_PROPERTIES = "../test-salesforce-login.properties"; +public final class LoginConfigHelper { - private static final LoginConfigHelper INSTANCE; + private static final LoginConfigHelper INSTANCE = new LoginConfigHelper(); - static { + private final Map<String, String> configuration; + + private LoginConfigHelper() { + configuration = new HashMap<>(); try { - INSTANCE = new LoginConfigHelper(); - } catch (final IOException e) { - throw new ExceptionInInitializerError(e); + final ResourceBundle properties = ResourceBundle.getBundle("test-salesforce-login"); + properties.keySet().forEach(k -> configuration.put(k, properties.getString(k))); + } catch (final MissingResourceException ignored) { + // ignoring if missing } + + System.getenv().keySet().stream()// + .filter(k -> k.startsWith("SALESFORCE_") && isNotEmpty(System.getenv(k))) + .forEach(k -> configuration.put(fromEnvName(k), System.getenv(k))); + System.getProperties().keySet().stream().map(String.class::cast) + .filter(k -> k.startsWith("salesforce.") && isNotEmpty(System.getProperty(k))) + .forEach(k -> configuration.put(k, System.getProperty(k))); } - private SalesforceLoginConfig config; + private String fromEnvName(final String envVariable) { + return envVariable.replaceAll("_", ".").toLowerCase(); + } - private final Properties properties; + SalesforceLoginConfig createLoginConfig() { + final SalesforceLoginConfig loginConfig = new SalesforceLoginConfig(); - public LoginConfigHelper() throws IOException { - // load test-salesforce-login properties - try (InputStream stream = new FileInputStream(TEST_LOGIN_PROPERTIES);) { - properties = new Properties(); - properties.load(stream); - } catch (final FileNotFoundException ignored) { - throw new FileNotFoundException("Create a properties file named " + TEST_LOGIN_PROPERTIES - + " with clientId, clientSecret, userName, and password or with clientId, clientSecret and refreshToken" - + " for a Salesforce account with Merchandise and Invoice objects from Salesforce Guides."); + final String explicitType = configuration.get("salesforce.auth.type"); + if (ObjectHelper.isNotEmpty(explicitType)) { + loginConfig.setType(AuthenticationType.valueOf(explicitType)); } + loginConfig.setClientId(configuration.get("salesforce.client.id")); + loginConfig.setClientSecret(configuration.get("salesforce.client.secret")); + loginConfig.setUserName(configuration.get("salesforce.username")); + loginConfig.setPassword(configuration.get("salesforce.password")); - final boolean hasPassword = ObjectHelper.isNotEmpty(properties.getProperty("password")); - final boolean hasRefreshToken = ObjectHelper.isNotEmpty(properties.getProperty("refreshToken")); - final boolean hasKeystore = ObjectHelper.isNotEmpty(properties.getProperty("keystore.resource")); - - final boolean lazyLogin = Boolean.parseBoolean(properties.getProperty("lazyLogin", "false")); - final String loginUrl = properties.getProperty("loginUrl", SalesforceLoginConfig.DEFAULT_LOGIN_URL); - final String clientId = properties.getProperty("clientId"); - final String clientSecret = properties.getProperty("clientSecret"); - final String username = properties.getProperty("userName"); - - if (hasPassword) { - config = new SalesforceLoginConfig(loginUrl, clientId, clientSecret, username, - properties.getProperty("password"), lazyLogin); - } else if (hasRefreshToken) { - config = new SalesforceLoginConfig(loginUrl, clientId, // - clientSecret, // - properties.getProperty("refreshToken"), // - lazyLogin); - } else if (hasKeystore) { - final KeyStoreParameters keystore = new KeyStoreParameters(); - keystore.setResource(properties.getProperty("keystore.resource")); - keystore.setType(properties.getProperty("keystore.type")); - keystore.setPassword(properties.getProperty("keystore.password")); - config = new SalesforceLoginConfig(loginUrl, clientId, username, keystore, lazyLogin); - } else { - throw new IllegalArgumentException("Must specifiy parameters in " + TEST_LOGIN_PROPERTIES); - } + final KeyStoreParameters keystore = new KeyStoreParameters(); + keystore.setResource(configuration.get("salesforce.keystore.resource")); + keystore.setPassword(configuration.get("salesforce.keystore.password")); + keystore.setType(configuration.get("salesforce.keystore.type")); + keystore.setProvider(configuration.get("salesforce.keystore.provider")); + loginConfig.setKeystore(keystore); + + validate(loginConfig); + return loginConfig; } - public static SalesforceLoginConfig getLoginConfig() { - return INSTANCE.config; + void validate(final SalesforceLoginConfig loginConfig) { + try { + loginConfig.validate(); + } catch (final IllegalArgumentException e) { + System.out.println("To run integration tests Salesforce Authentication information is"); + System.out.println("needed."); + System.out.println("You need to specify the configuration for running tests by either"); + System.out.println("specifying environment variables, Maven properties or create a Java"); + System.out.println("properties file at:"); + System.out.println(); + System.out.println("camel/components/camel-salesforce/test-salesforce-login.properties"); + System.out.println(); + System.out.println("With authentication information to access a Salesforce instance."); + System.out.println("You can use:"); + System.out.println(); + System.out.println("camel/components/camel-salesforce/test-salesforce-login.properties.sample"); + System.out.println(); + System.out.println("as reference. A free Salesforce developer account can be obtained at:"); + System.out.println(); + System.out.println("https://developer.salesforce.com"); + System.out.println(); + System.out.println("Properties that you need to set:"); + System.out.println(); + System.out.println("| Maven or properties file | Environment variable | Use |"); + System.out.println("|------------------------------+------------------------------+--------|"); + System.out.println("| salesforce.client.id | SALESFORCE_CLIENT_ID | ALL |"); + System.out.println("| salesforce.client.secret | SALESFORCE_CLIENT_SECRET | UP, RT |"); + System.out.println("| salesforce.username | SALESFORCE_USERNAME | UP, JWT|"); + System.out.println("| salesforce.password | SALESFORCE_PASSWORD | UP |"); + System.out.println("| salesforce.refreshToken | SALESFORCE_REFRESH_TOKEN | RT |"); + System.out.println("| salesforce.keystore.path | SALESFORCE_KEYSTORE_PATH | JWT |"); + System.out.println("| salesforce.keystore.type | SALESFORCE_KEYSTORE_TYPE | JWT |"); + System.out.println("| salesforce.keystore.password | SALESFORCE_KEYSTORE_PASSWORD | JWT |"); + System.out.println("| salesforce.login.url | SALESFORCE_LOGIN_URL | ALL |"); + System.out.println(); + System.out.println("* ALL - required allways"); + System.out.println("* UP - when using username and password authentication"); + System.out.println("* RT - when using refresh token flow"); + System.out.println("* JWT - when using JWT flow"); + System.out.println(); + System.out.println("You can force authentication type to be one of USERNAME_PASSWORD,"); + System.out.println("REFRESH_TOKEN or JWT by setting `salesforce.auth.type` (or "); + System.out.println("`SALESFORCE_AUTH_TYPE` for environment variables)."); + System.out.println(); + System.out.println("Examples:"); + System.out.println(); + System.out.println("Using environment:"); + System.out.println(); + System.out.println("$ export SALESFORCE_CLIENT_ID=..."); + System.out.println("$ export SALESFORCE_CLIENT_SECRET=..."); + System.out.println("$ export ...others..."); + System.out.println(); + System.out.println("or using Maven properties:"); + System.out.println(); + System.out.println("$ mvn -Pintegration -Dsalesforce.client.id=... \\"); + System.out.println(" -Dsalesforce.client.secret=... ..."); + System.out.println(); + } } - public static Properties testLoginProperties() { - return INSTANCE.properties; + public static SalesforceLoginConfig getLoginConfig() { + return INSTANCE.createLoginConfig(); } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceJwtBearerTokenFlowIntegrationTest.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceJwtBearerTokenFlowIntegrationTest.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceJwtBearerTokenFlowIntegrationTest.java index 5a799c9..050ce3e 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceJwtBearerTokenFlowIntegrationTest.java +++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceJwtBearerTokenFlowIntegrationTest.java @@ -16,14 +16,13 @@ */ package org.apache.camel.component.salesforce; -import java.util.Properties; - import org.apache.camel.CamelContext; import org.apache.camel.component.salesforce.api.dto.Limits; import org.apache.camel.test.junit4.CamelTestSupport; -import org.apache.camel.util.jsse.KeyStoreParameters; import org.junit.Test; +import static org.junit.Assume.assumeNotNull; + public class SalesforceJwtBearerTokenFlowIntegrationTest extends CamelTestSupport { @Test @@ -38,16 +37,14 @@ public class SalesforceJwtBearerTokenFlowIntegrationTest extends CamelTestSuppor final CamelContext camelContext = super.createCamelContext(); final SalesforceComponent salesforce = new SalesforceComponent(); - final Properties properties = LoginConfigHelper.testLoginProperties(); - salesforce.setClientId(properties.getProperty("clientId")); - salesforce.setUserName(properties.getProperty("userName")); - salesforce.setLoginUrl(properties.getProperty("loginUrl")); - - KeyStoreParameters keystore = new KeyStoreParameters(); - keystore.setResource(properties.getProperty("keystore.resource")); - keystore.setType(properties.getProperty("keystore.type")); - keystore.setPassword(properties.getProperty("keystore.password")); - salesforce.setKeystore(keystore); + final SalesforceLoginConfig loginConfig = LoginConfigHelper.getLoginConfig(); + + assumeNotNull(loginConfig.getKeystore()); + assumeNotNull(loginConfig.getKeystore().getResource()); + // force authentication type to JWT + loginConfig.setType(AuthenticationType.JWT); + + salesforce.setLoginConfig(loginConfig); camelContext.addComponent("salesforce", salesforce); http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/components/camel-salesforce/pom.xml ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/pom.xml b/components/camel-salesforce/pom.xml index dea2bb3..629b02c 100644 --- a/components/camel-salesforce/pom.xml +++ b/components/camel-salesforce/pom.xml @@ -15,31 +15,32 @@ See the License for the specific language governing permissions and limitations under the License. --> -<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"> +<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"> - <modelVersion>4.0.0</modelVersion> + <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>org.apache.camel</groupId> - <artifactId>components</artifactId> - <version>2.19.0-SNAPSHOT</version> - </parent> + <parent> + <groupId>org.apache.camel</groupId> + <artifactId>components</artifactId> + <version>2.19.0-SNAPSHOT</version> + </parent> - <artifactId>camel-salesforce-parent</artifactId> - <packaging>pom</packaging> - <name>Camel :: Salesforce :: Parent</name> - <description>Camel Salesforce parent</description> + <artifactId>camel-salesforce-parent</artifactId> + <packaging>pom</packaging> + <name>Camel :: Salesforce :: Parent</name> + <description>Camel Salesforce parent</description> - <modules> - <module>camel-salesforce-component</module> - <module>camel-salesforce-maven-plugin</module> - </modules> + <modules> + <module>camel-salesforce-component</module> + <module>camel-salesforce-maven-plugin</module> + </modules> - <properties> - <salesforce.component.root>${project.basedir}</salesforce.component.root> - </properties> + <properties> + <salesforce.component.root>${project.basedir}</salesforce.component.root> + </properties> - <build> + <build> <plugins> <plugin> <groupId>org.apache.camel</groupId> @@ -56,87 +57,17 @@ <goal>prepare-components</goal> </goals> <phase>generate-resources</phase> - </execution> + </execution> <execution> <id>validate</id> <goals> <goal>validate-components</goal> </goals> <phase>prepare-package</phase> - </execution> + </execution> </executions> </plugin> </plugins> </build> - <profiles> - - <profile> - <id>integration</id> - <build> - <plugins> - <plugin> - <artifactId>maven-enforcer-plugin</artifactId> - <executions> - <execution> - <id>salesforce-integration-test-prerequisites</id> - <goals> - <goal>enforce</goal> - </goals> - <configuration> - <rules> - <requireFilesExist> - <files> - <file>${salesforce.component.root}/it/resources/migration-tool/ant-salesforce.jar</file> - </files> - <message><![CDATA[Salesforce Migration Tool required - -You need to download the Salesforce Migration Tool (ZIP file) and -extract the `ant-salesforce.jar` out of it to: - -${salesforce.component.root}/it/resources/migration-tool/ant-salesforce.jar - -It's needed by the integration tests to setup the Salesforce instance -with custom sObjects/fields that are required by the tests. - -Have a look at: - -${salesforce.component.root}/it/resources/salesforce - -To see what will be done. - -For information and download of the Salesforce Migration Tool consult: - -https://developer.salesforce.com/page/Force.com_Migration_Tool]]></message> - </requireFilesExist> - <requireFilesExist> - <files> - <file>${salesforce.component.root}/test-salesforce-login.properties</file> - </files> - <message><![CDATA[Salesforce Authentication information needed - -You need to create a Java properties file at: - -${salesforce.component.root}/test-salesforce-login.properties - -With authentication information to access a Salesforce instance. -You can use: - -${salesforce.component.root}/test-salesforce-login.properties.sample - -as reference. A free Salesforce developer account can be obtained at: - -https://developer.salesforce.com]]></message> - </requireFilesExist> - </rules> - <fail>true</fail> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - </profiles> - </project> http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/components/camel-salesforce/test-salesforce-login.properties.sample ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/test-salesforce-login.properties.sample b/components/camel-salesforce/test-salesforce-login.properties.sample index dc268f6..2b79643 100644 --- a/components/camel-salesforce/test-salesforce-login.properties.sample +++ b/components/camel-salesforce/test-salesforce-login.properties.sample @@ -37,34 +37,53 @@ # component but it makes it easier to obtain the refresh token. # From there make note of the parameters and fill in below: +# You can leave the authentication type undefined the component +# auto-determines the type by the specified properties, or you can force +# the authentication type to be of certain type if you have specified +# properties that conform to multiple authenticationt types and the +# aut-determination does not make sense +# salesforce.auth.type = USERNAME_PASSWORD + # This is the Consumer Key of the connected application -# This `clientId` is setup by the migration run at the start of +# This `salesforce.client.id` is setup by the migration run at the start of # integration tests (when run from Maven): -# clientId = 3MVG9HxRZv05HarQ5D2to.ylPaUg7uaFqGqE2wN6_RRaHM9PTa3SWT8UwKJzXAyCujHrfObkUy7oZqiBUyfGl +# salesforce.client.id = 3MVG9HxRZv05HarQ5D2to.ylPaUg7uaFqGqE2wN6_RRaHM9PTa3SWT8UwKJzXAyCujHrfObkUy7oZqiBUyfGl # -clientId = <Your Consumer Key> +salesforce.client.id = <Your Consumer Key> # This is Consumer Secret of the connected application # If you use the above clientId (3MV...Gl) get the Consumer Secret from # your Salesforce instance after migration run, access your Salesforce # instance and get the Consumer Secret from there for # CamelSalesforceIntegrationTests connected application -clientSecret = <Your Consumer Secret> +salesforce.client.secret = <Your Consumer Secret> # This is the username for the Salesforce account, you can use your own # developer account, or add another user to your Salesforce instance, # but make sure that it has administrative rights -userName = <Your Salesforce username> +salesforce.username = <Your Salesforce username> -# You can use refresh token instead of userName and password parameters -refreshToken = <Your Refresh token> +# You can use refresh token instead of username and password parameters +salesforce.refreshToken = <Your Refresh token> # Password of the user above, if you're using security tokens, generate # one and append it to the end of the password -password = <Your Salesforce password>[Your security token, if used] +salesforce.password = <Your Salesforce password>[Your security token, if used] -# This should be https://login.salesforce.com/, change if appropriate -loginUrl = https://login.salesforce.com/ +# For JWT OAuth flow don't specify password and refreshToken and specify +# the following properties: + +# path to the keystore +salesforce.keystore.resource= + +# type of the keystore +salesforce.keystore.type= -# Remains, TODO remove this -report.0 = Test_Report +# password of the keystore +salesforce.keystore.password= + +# JCE provider of the keystore +salesforce.keystore.provider= + +# This should be https://login.salesforce.com/, change if appropriate +salesforce.login.url = https://login.salesforce.com/ http://git-wip-us.apache.org/repos/asf/camel/blob/c63a9fd2/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java ---------------------------------------------------------------------- diff --git a/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java b/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java index 364e405..22fa14e 100644 --- a/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java +++ b/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.camel.component.salesforce.AuthenticationType; import org.apache.camel.component.salesforce.SalesforceHttpClient; import org.apache.camel.component.salesforce.api.dto.analytics.reports.ReportMetadata; import org.apache.camel.component.salesforce.api.dto.approval.ApprovalRequest; @@ -43,6 +44,11 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty; public class SalesforceComponentConfiguration { /** + * Explicit authentication type to be used one of USERNAME_PASSWORD + * REFRESH_TOKEN or JWT. + */ + private AuthenticationType authenticationType; + /** * To use the shared SalesforceLoginConfig as login configuration */ private SalesforceLoginConfigNestedConfiguration loginConfig; @@ -149,6 +155,14 @@ public class SalesforceComponentConfiguration { */ private Boolean resolvePropertyPlaceholders = true; + public AuthenticationType getAuthenticationType() { + return authenticationType; + } + + public void setAuthenticationType(AuthenticationType authenticationType) { + this.authenticationType = authenticationType; + } + public SalesforceLoginConfigNestedConfiguration getLoginConfig() { return loginConfig; } @@ -378,6 +392,7 @@ public class SalesforceComponentConfiguration { * Salesforce connected application Consumer token */ private String refreshToken; + private AuthenticationType type; /** * Salesforce account user name */ @@ -433,6 +448,14 @@ public class SalesforceComponentConfiguration { this.refreshToken = refreshToken; } + public AuthenticationType getType() { + return type; + } + + public void setType(AuthenticationType type) { + this.type = type; + } + public String getUserName() { return userName; }