package org.ovirt.engine.core.bll.provider; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.cert.Certificate; import java.util.List; import javax.net.ssl.SSLException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.exception.ExceptionUtils; import org.ovirt.engine.core.common.businessentities.Provider; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.errors.EngineError; import org.ovirt.engine.core.common.errors.EngineException; import org.ovirt.engine.core.utils.EngineLocalConfig; import org.ovirt.engine.core.uutils.crypto.CertificateChain; import org.ovirt.engine.core.uutils.net.HttpURLConnectionBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class BaseProviderProxy implements ProviderProxy { protected final Logger log = LoggerFactory.getLogger(getClass()); private URL url; private Provider<?> hostProvider; private ProviderValidator providerValidator; protected enum HttpMethodType { GET, POST, PUT, DELETE } public BaseProviderProxy(Provider<?> provider) { try { url = new URL(provider.getUrl()); this.hostProvider = provider; } catch (MalformedURLException e) { handleException(e); } } public URL getUrl() { return url; } @Override public List<? extends Certificate> getCertificateChain() { List<? extends Certificate> result = null; if (url.getProtocol().equalsIgnoreCase(String.valueOf("https"))) { try { result = CertificateChain.completeChain( CertificateChain.getSSLPeerCertificates(url), CertificateChain.keyStoreToTrustAnchors( ExternalTrustStoreInitializer.getTrustStore())); } catch (IOException | GeneralSecurityException e) { log.error("Failed to communicate with external provider '{}' due to error '{}'", hostProvider.getName(), ExceptionUtils.getRootCauseMessage(e)); log.debug("Exception", e); handleException(e); } } return result; } protected static void handleException(Exception e) { throw new EngineException(EngineError.PROVIDER_FAILURE, e.getMessage()); } protected void afterReadResponse(HttpURLConnection connection, byte[] response) throws Exception { } protected void beforeReadResponse(HttpURLConnection connection) throws Exception { if (connection.getResponseCode() != HttpURLConnection.HTTP_OK && connection.getResponseCode() != HttpURLConnection.HTTP_MOVED_TEMP) { throw new EngineException(EngineError.PROVIDER_FAILURE); } } protected HttpURLConnection createConnection(String relativePath) { URL hostUrl = getUrl(); HttpURLConnectionBuilder builder; HttpURLConnection result; try { builder = new HttpURLConnectionBuilder().appendRelativePath(hostUrl, relativePath); if (new File(EngineLocalConfig.getInstance().getExternalProvidersTrustStore().getAbsolutePath()).exists()) { builder .setTrustStore( EngineLocalConfig.getInstance().getExternalProvidersTrustStore().getAbsolutePath()) .setTrustStorePassword(EngineLocalConfig.getInstance().getExternalProvidersTrustStorePassword()) .setTrustStoreType(EngineLocalConfig.getInstance().getExternalProvidersTrustStoreType()) .setHttpsProtocol(Config.getValue(ConfigValues.ExternalCommunicationProtocol)); } result = builder.create(); handleCredentials(result); } catch (Exception ex) { log.error("Cannot communicate with provider", ex); throw new EngineException(EngineError.PROVIDER_FAILURE); } return result; } public byte[] getResponse(HttpURLConnection connection) throws IOException { byte[] response; ByteArrayOutputStream bytesOs = new ByteArrayOutputStream(); try (BufferedInputStream bis = new BufferedInputStream(connection.getInputStream())) { beforeReadResponse(connection); byte[] buff = new byte[8196]; while (true) { int read = bis.read(buff, 0, 8196); if (read > 0) { bytesOs.write(buff, 0, read); } else { break; } } response = bytesOs.toByteArray(); afterReadResponse(connection, response); } catch (Exception ex) { log.error("Exception is {} ", ex.getMessage()); log.debug("Exception: ", ex); if (ex instanceof EngineException) { throw (EngineException) ex; } else { throw new EngineException(EngineError.PROVIDER_FAILURE, ex.getMessage()); } } return response; } protected void handleCredentials(HttpURLConnection connection) { if (hostProvider.getUsername() != null && hostProvider.getPassword() != null) { connection.setRequestProperty( "Authorization", String.format("Basic %1$s", new Base64(0).encodeToString( String.format("%1$s:%2$s", hostProvider.getUsername(), hostProvider.getPassword()) .getBytes(StandardCharsets.UTF_8)) ) ); } } protected byte[] runHttpMethod(HttpMethodType httpMethod, String contentType, String body, HttpURLConnection connection) { byte[] result = null; try { connection.setRequestProperty("Content-Type", contentType); connection.setDoInput(true); connection.setDoOutput(httpMethod != HttpMethodType.GET); connection.setRequestMethod(httpMethod.toString()); if (body != null) { byte[] bytes = body.getBytes(StandardCharsets.UTF_8); connection.setRequestProperty("Content-Length", String.valueOf(bytes.length)); try (OutputStream outputStream = connection.getOutputStream()) { outputStream.write(bytes); } } result = getResponse(connection); } catch (SSLException e) { throw new EngineException(EngineError.PROVIDER_SSL_FAILURE, e.getMessage()); } catch (IOException e) { handleException(e); } finally { if (connection != null) { connection.disconnect(); } } return result; } @Override public ProviderValidator getProviderValidator() { if (providerValidator == null) { providerValidator = new ProviderValidator(hostProvider); } return providerValidator; } public Provider<?> getProvider() { return hostProvider; } }