blob: 71f1acafdee5ffa7dd670221a4933fbace4e0418 [file] [log] [blame]
/**
* 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.box.internal;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.security.GeneralSecurityException;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import com.box.boxjavalibv2.BoxClient;
import com.box.boxjavalibv2.BoxConnectionManagerBuilder;
import com.box.boxjavalibv2.BoxRESTClient;
import com.box.boxjavalibv2.authorization.IAuthFlowUI;
import com.box.boxjavalibv2.authorization.IAuthSecureStorage;
import com.box.restclientv2.IBoxRESTClient;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.component.box.BoxConfiguration;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.jsse.SSLContextParameters;
import org.apache.http.HttpHost;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper class to work with {@link BoxClient}.
*/
public final class BoxClientHelper {
private static final Logger LOG = LoggerFactory.getLogger(BoxClientHelper.class);
private BoxClientHelper() {
}
// create BoxClient using provided configuration
@SuppressWarnings("deprecation")
public static CachedBoxClient createBoxClient(final BoxConfiguration configuration) {
final String clientId = configuration.getClientId();
final String clientSecret = configuration.getClientSecret();
final IAuthSecureStorage authSecureStorage = configuration.getAuthSecureStorage();
final String userName = configuration.getUserName();
final String userPassword = configuration.getUserPassword();
if ((authSecureStorage == null && ObjectHelper.isEmpty(userPassword))
|| ObjectHelper.isEmpty(userName) || ObjectHelper.isEmpty(clientId) || ObjectHelper.isEmpty(clientSecret)) {
throw new IllegalArgumentException(
"Missing one or more required properties "
+ "clientId, clientSecret, userName and either authSecureStorage or userPassword");
}
LOG.debug("Creating BoxClient for login:{}, client_id:{} ...", userName, clientId);
// if set, use configured connection manager builder
final BoxConnectionManagerBuilder connectionManagerBuilder = configuration.getConnectionManagerBuilder();
final BoxConnectionManagerBuilder connectionManager = connectionManagerBuilder != null
? connectionManagerBuilder : new BoxConnectionManagerBuilder();
// create REST client for BoxClient
final ClientConnectionManager[] clientConnectionManager = new ClientConnectionManager[1];
final IBoxRESTClient restClient = new BoxRESTClient(connectionManager.build()) {
@Override
public HttpClient getRawHttpClient() {
final HttpClient httpClient = super.getRawHttpClient();
clientConnectionManager[0] = httpClient.getConnectionManager();
final SchemeRegistry schemeRegistry = clientConnectionManager[0].getSchemeRegistry();
SSLContextParameters sslContextParameters = configuration.getSslContextParameters();
if (sslContextParameters == null) {
sslContextParameters = new SSLContextParameters();
}
final Map<String, Object> configParams = configuration.getHttpParams();
boolean useSocksProxy = false;
HttpHost proxyHost = null;
if (configParams != null && !configParams.isEmpty()) {
final Boolean socksProxy = (Boolean) configParams.get("http.route.socks-proxy");
if (socksProxy != null && socksProxy) {
useSocksProxy = true;
proxyHost = (HttpHost) configParams.get(ConnRoutePNames.DEFAULT_PROXY);
}
// set custom HTTP params
LOG.debug("Setting {} HTTP Params", configParams.size());
final HttpParams httpParams = httpClient.getParams();
for (Map.Entry<String, Object> param : configParams.entrySet()) {
// don't add proxy params if socks
if (!(useSocksProxy && (param.getKey().equals("http.route.socks-proxy") || param.getKey().equals(ConnRoutePNames.DEFAULT_PROXY)))) {
httpParams.setParameter(param.getKey(), param.getValue());
}
}
}
SSLContext sslContext;
try {
sslContext = sslContextParameters.createSSLContext();
} catch (IOException e) {
throw ObjectHelper.wrapRuntimeCamelException(e);
} catch (GeneralSecurityException e) {
throw ObjectHelper.wrapRuntimeCamelException(e);
}
SSLSocketFactory socketFactory;
if (useSocksProxy) {
socketFactory = new SocksSSLSocketFactory(sslContext, proxyHost);
} else {
socketFactory = new SSLSocketFactory(sslContext, SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
}
schemeRegistry.register(new Scheme("https", socketFactory, 443));
return httpClient;
}
};
final BoxClient boxClient = new BoxClient(clientId, clientSecret, null, null,
restClient, configuration.getBoxConfig());
// enable OAuth auto-refresh
boxClient.setAutoRefreshOAuth(true);
// wrap the configured storage in a caching storage
final CachingSecureStorage storage = new CachingSecureStorage(authSecureStorage);
// set up a listener to notify secure storage and user provided listener, store it in configuration!
final OAuthHelperListener listener = new OAuthHelperListener(storage, configuration.getRefreshListener());
boxClient.addOAuthRefreshListener(listener);
final CachedBoxClient cachedBoxClient = new CachedBoxClient(boxClient, userName, clientId, storage, listener, clientConnectionManager);
LOG.debug("BoxClient created {}", cachedBoxClient);
return cachedBoxClient;
}
public static void getOAuthToken(BoxConfiguration configuration, CachedBoxClient cachedBoxClient) throws Exception {
final BoxClient boxClient = cachedBoxClient.getBoxClient();
synchronized (boxClient) {
if (boxClient.isAuthenticated()) {
return;
}
LOG.debug("Getting OAuth token for {}...", cachedBoxClient);
final IAuthSecureStorage authSecureStorage = cachedBoxClient.getSecureStorage();
if (authSecureStorage != null && authSecureStorage.getAuth() != null) {
LOG.debug("Using secure storage for {}", cachedBoxClient);
// authenticate using stored refresh token
boxClient.authenticateFromSecureStorage(authSecureStorage);
} else {
LOG.debug("Using OAuth {}", cachedBoxClient);
// authorize App for user, and create OAuth token with refresh token
final IAuthFlowUI authFlowUI = new LoginAuthFlowUI(configuration, boxClient);
final CountDownLatch latch = new CountDownLatch(1);
final LoginAuthFlowListener listener = new LoginAuthFlowListener(latch);
boxClient.authenticate(authFlowUI, true, listener);
// wait for login to finish or timeout
if (!latch.await(configuration.getLoginTimeout(), TimeUnit.SECONDS)) {
if (!boxClient.isAuthenticated()) {
throw new RuntimeCamelException(String.format("Login timeout for %s", cachedBoxClient));
}
}
final Exception ex = listener.getException();
if (ex != null) {
throw new RuntimeCamelException(String.format("Login error for %s: %s",
cachedBoxClient, ex.getMessage()), ex);
}
}
LOG.debug("OAuth token created for {}", cachedBoxClient);
// notify the cached client listener for the first time, since BoxClient doesn't!!!
cachedBoxClient.getListener().onRefresh(boxClient.getAuthData());
}
}
public static void closeIdleConnections(CachedBoxClient cachedBoxClient) {
@SuppressWarnings("deprecation")
final ClientConnectionManager connectionManager = cachedBoxClient.getClientConnectionManager();
if (connectionManager != null) {
// close all idle connections
connectionManager.closeIdleConnections(1, TimeUnit.MILLISECONDS);
}
}
public static void shutdownBoxClient(BoxConfiguration configuration, CachedBoxClient cachedBoxClient) throws Exception {
final BoxClient boxClient = cachedBoxClient.getBoxClient();
synchronized (boxClient) {
LOG.debug("Shutting down {} ...", cachedBoxClient);
try {
// revoke token if requested
if (configuration.isRevokeOnShutdown()) {
revokeOAuthToken(configuration, cachedBoxClient);
}
} finally {
boxClient.setConnectionOpen(false);
// close connections in the underlying HttpClient
@SuppressWarnings("deprecation")
final ClientConnectionManager connectionManager = cachedBoxClient.getClientConnectionManager();
if (connectionManager != null) {
LOG.debug("Closing connections for {}", cachedBoxClient);
connectionManager.shutdown();
} else {
LOG.debug("ConnectionManager not created for {}", cachedBoxClient);
}
}
LOG.debug("Shutdown successful for {}", cachedBoxClient);
}
}
private static void revokeOAuthToken(BoxConfiguration configuration, CachedBoxClient cachedBoxClient) throws Exception {
final BoxClient boxClient = cachedBoxClient.getBoxClient();
synchronized (boxClient) {
if (boxClient.isAuthenticated()) {
LOG.debug("Revoking OAuth refresh token for {}", cachedBoxClient);
// revoke OAuth token
boxClient.getOAuthManager().revokeOAuth(boxClient.getAuthData().getAccessToken(),
configuration.getClientId(), configuration.getClientSecret());
// notify the OAuthListener of revoked token
cachedBoxClient.getListener().onRefresh(null);
// mark auth data revoked
boxClient.getOAuthDataController().setOAuthData(null);
}
}
}
static class SocksSSLSocketFactory extends SSLSocketFactory {
HttpHost proxyHost;
public SocksSSLSocketFactory(SSLContext sslContext, HttpHost proxyHost) {
super(sslContext, SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
this.proxyHost = proxyHost;
}
@Override
public Socket createSocket(final HttpContext context) throws IOException {
InetSocketAddress socksaddr = new InetSocketAddress(proxyHost.getHostName(), proxyHost.getPort());
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
return new Socket(proxy);
}
}
}