package com.github.scribejava.apis; import javax.net.ssl.SSLContext; import com.github.scribejava.apis.salesforce.SalesforceJsonTokenExtractor; import com.github.scribejava.core.builder.api.DefaultApi20; import com.github.scribejava.core.extractors.TokenExtractor; import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.model.Verb; import java.io.IOException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import javax.net.ssl.SSLSocket; /** * This class is an implementation of the Salesforce OAuth2 API. * The default implementation connects to the Salesforce * production environment. * If you want to connect to a Sandbox environment you've to use {@link #sandbox()} method to * get sandbox instance of this API */ public class SalesforceApi extends DefaultApi20 { private static final String PRODUCTION_HOST = "login.salesforce.com"; private static final String SANDBOX_HOST = "test.salesforce.com"; private static final String PROTOCOL = "https://"; private static final String ACCESS_PATH = "/services/oauth2/token"; private static final String AUTHORIZE_PATH = "/services/oauth2/authorize"; private final String accessTokenUrl; private final String authorizationBaseUrl; /** * @param hostName The hostname to be used, which is either {@link #PRODUCTION_HOST} or {@link #SANDBOX_HOST}. */ protected SalesforceApi(String hostName) { accessTokenUrl = PROTOCOL + hostName + ACCESS_PATH; authorizationBaseUrl = PROTOCOL + hostName + AUTHORIZE_PATH; try { final SSLSocket socket = (SSLSocket) SSLContext.getDefault().getSocketFactory().createSocket(); if (!isTLSv11orUpperEnabled(socket)) { throw new IllegalStateException("Salesforce API required to use TLSv1.1 or upper. " + "Enabled it by invoking method initTLSv11orUpper or somehow else"); } } catch (NoSuchAlgorithmException | IOException ex) { throw new IllegalStateException("Salesforce API required to use TLSv1.1 or upper. " + "Enabled it by invoking method initTLSv11orUpper or somehow else"); } } private static class InstanceHolder { private static final SalesforceApi INSTANCE = new SalesforceApi(PRODUCTION_HOST); } public static SalesforceApi instance() { return InstanceHolder.INSTANCE; } public static SalesforceApi sandbox() { return new SalesforceApi(SANDBOX_HOST); } @Override public Verb getAccessTokenVerb() { return Verb.POST; } @Override public String getAccessTokenEndpoint() { return accessTokenUrl; } @Override protected String getAuthorizationBaseUrl() { return authorizationBaseUrl; } @Override public TokenExtractor<OAuth2AccessToken> getAccessTokenExtractor() { return SalesforceJsonTokenExtractor.instance(); } private static boolean isTLSv11orUpperEnabled(final SSLSocket socket) { for (String protocol : socket.getEnabledProtocols()) { if ("TLSv1.2".equals(protocol) || "TLSv1.1".equals(protocol)) { return true; } } return false; } /** * Salesforce API requires to use TLSv1.1 or upper. * <p> * Java 8 have TLS 1.2 enabled by default. java 7 - no, you should invoke this method or turn TLS>=1.1 somehow * else</p> * * @throws java.security.NoSuchAlgorithmException in case your jvm doesn't support TLSv1.1 and TLSv1.2 * @throws java.security.KeyManagementException unexpected Exception from * {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom)} * @throws java.io.IOException unexpected Exception from {@link javax.net.ssl.SSLSocketFactory#createSocket()} */ public static void initTLSv11orUpper() throws NoSuchAlgorithmException, KeyManagementException, IOException { final SSLSocket socket = (SSLSocket) SSLContext.getDefault().getSocketFactory().createSocket(); if (isTLSv11orUpperEnabled(socket)) { return; } boolean supportTLSv11 = false; boolean supportTLSv12 = false; for (String protocol : socket.getSupportedProtocols()) { if ("TLSv1.2".equals(protocol)) { supportTLSv12 = true; break; } if ("TLSv1.1".equals(protocol)) { supportTLSv11 = true; } } final SSLContext context; if (supportTLSv12) { context = SSLContext.getInstance("TLSv1.2"); } else if (supportTLSv11) { context = SSLContext.getInstance("TLSv1.1"); } else { throw new NoSuchAlgorithmException("for Salesforce API to work you need jvm with TLS 1.1 or 1.2 support"); } context.init(null, null, null); SSLContext.setDefault(context); } }