
com.sap.cloud.sdk.cloudplatform.connectivity.ScpNeoDestination Maven / Gradle / Ivy
Show all versions of connectivity-scp-neo Show documentation
/*
* Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved.
*/
package com.sap.cloud.sdk.cloudplatform.connectivity;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import com.google.common.base.Strings;
import com.netflix.hystrix.exception.HystrixBadRequestException;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import com.sap.cloud.sdk.cloudplatform.CloudPlatform;
import com.sap.cloud.sdk.cloudplatform.CloudPlatformAccessor;
import com.sap.cloud.sdk.cloudplatform.ScpNeoCloudPlatform;
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException;
import com.sap.cloud.sdk.cloudplatform.exception.CloudPlatformException;
import com.sap.cloud.sdk.cloudplatform.exception.ShouldNotHappenException;
import com.sap.cloud.sdk.cloudplatform.logging.CloudLoggerFactory;
import com.sap.cloud.sdk.cloudplatform.security.BasicCredentials;
import com.sap.cloud.sdk.cloudplatform.tenant.ScpNeoTenant;
import com.sap.cloud.sdk.cloudplatform.tenant.Tenant;
import com.sap.cloud.sdk.cloudplatform.tenant.TenantAccessor;
import com.sap.core.connectivity.api.configuration.DestinationConfiguration;
import lombok.Getter;
/**
* Destination used with the Neo platform.
*
* @see ScpNeoGenericDestination
* @see ScpNeoRfcDestination
*/
public class ScpNeoDestination extends AbstractDestination
{
private static final Logger logger = CloudLoggerFactory.getLogger(ScpNeoDestination.class);
private static final int DEFAULT_HTTPS_PORT = 443;
private static final String SAP_CONNECTIVITY_CONSUMER_ACCOUNT = "SAP-Connectivity-ConsumerAccount";
private static final String SAP_CONNECTIVITY_SCC_LOCATION_ID_HEADER = "SAP-Connectivity-SCC-Location_ID";
@Getter
@Nullable
private final DestinationConfiguration destinationConfiguration;
@Nullable
private final String cloudConnectorLocationId;
/**
* Creates a destination to be used on SAP Cloud Platform Neo.
*
* @param destinationConfiguration
* The destination configuration as received by the Neo container.
* @param name
* The name of the destination.
* @param description
* A description of this destination.
* @param uri
* The uri of this destination.
* @param authenticationType
* The {@code AuthenticationType} of this destination.
* @param basicCredentials
* The credentials to be used if {@code authenticationType} is set to {@code BASIC_AUTHENTICATION}.
* @param proxyType
* The type of proxy to be used for this destination.
* @param proxyConfiguration
* The configuration of the proxy to be used (if given).
* @param isTrustingAllCertificates
* Flag indicating whether all certificates should be accepted when communicating with the destination.
* @param trustStoreLocation
* The name of the trust store to search for in the {@code destinationConfiguration}.
* @param trustStorePassword
* The password to access the trust store.
* @param keyStoreLocation
* The name of the key store to search for in the {@code destinationConfiguration}.
* @param keyStorePassword
* The password to access the key store.
* @param cloudConnectorLocationId
* The id to be used when communicating in {@code ON_PREMISE} {@code proxyType} with an on-premise
* system.
* @param propertiesByName
* A map containing all additional properties.
*/
public ScpNeoDestination(
@Nullable final DestinationConfiguration destinationConfiguration,
@Nonnull final String name,
@Nullable final String description,
@Nonnull final URI uri,
@Nonnull final AuthenticationType authenticationType,
@Nullable final BasicCredentials basicCredentials,
@Nonnull final ProxyType proxyType,
@Nullable final ProxyConfiguration proxyConfiguration,
final boolean isTrustingAllCertificates,
@Nullable final String trustStoreLocation,
@Nullable final String trustStorePassword,
@Nullable final String keyStoreLocation,
@Nullable final String keyStorePassword,
@Nullable final String cloudConnectorLocationId,
@Nonnull final Map propertiesByName )
{
this(
destinationConfiguration,
name,
description,
uri.toString(),
authenticationType,
basicCredentials,
proxyType,
proxyConfiguration,
isTrustingAllCertificates,
trustStoreLocation,
trustStorePassword,
keyStoreLocation,
keyStorePassword,
cloudConnectorLocationId,
propertiesByName);
}
/**
* Creates a destination to be used on SAP Cloud Platform Neo.
*
* @param destinationConfiguration
* The destination configuration as received by the Neo container.
* @param name
* The name of the destination.
* @param description
* A description of this destination.
* @param uri
* The uri of this destination.
* @param authenticationType
* The {@code AuthenticationType} of this destination.
* @param basicCredentials
* The credentials to be used if {@code authenticationType} is set to {@code BASIC_AUTHENTICATION}.
* @param proxyType
* The type of proxy to be used for this destination.
* @param proxyConfiguration
* The configuration of the proxy to be used (if given).
* @param isTrustingAllCertificates
* Flag indicating whether all certificates should be accepted when communicating with the destination.
* @param trustStoreLocation
* The name of the trust store to search for in the {@code destinationConfiguration}.
* @param trustStorePassword
* The password to access the trust store.
* @param keyStoreLocation
* The name of the key store to search for in the {@code destinationConfiguration}.
* @param keyStorePassword
* The password to access the key store.
* @param cloudConnectorLocationId
* The id to be used when communicating in {@code ON_PREMISE} {@code proxyType} with an on-premise
* system.
* @param propertiesByName
* A map containing all additional properties.
*/
public ScpNeoDestination(
@Nullable final DestinationConfiguration destinationConfiguration,
@Nonnull final String name,
@Nullable final String description,
@Nonnull final String uri,
@Nonnull final AuthenticationType authenticationType,
@Nullable final BasicCredentials basicCredentials,
@Nonnull final ProxyType proxyType,
@Nullable final ProxyConfiguration proxyConfiguration,
final boolean isTrustingAllCertificates,
@Nullable final String trustStoreLocation,
@Nullable final String trustStorePassword,
@Nullable final String keyStoreLocation,
@Nullable final String keyStorePassword,
@Nullable final String cloudConnectorLocationId,
@Nonnull final Map propertiesByName )
{
super(
name,
description,
uri,
authenticationType,
basicCredentials,
proxyType,
proxyConfiguration,
isTrustingAllCertificates,
trustStoreLocation,
trustStorePassword,
keyStoreLocation,
keyStorePassword,
propertiesByName);
this.destinationConfiguration = destinationConfiguration;
this.cloudConnectorLocationId = cloudConnectorLocationId;
}
/**
* Creates a mocked {@link ScpNeoDestination} returning an empty destination name.
*
* This no-arguments constructor is required to ensure compatibility with mocking frameworks such as Mockito.
*/
private ScpNeoDestination()
{
this(
null,
"",
null,
URI.create(""),
AuthenticationType.NO_AUTHENTICATION,
null,
ProxyType.INTERNET,
null,
false,
null,
null,
null,
null,
null,
Collections.emptyMap());
}
/**
* Getter for the location identifier used by the SAP Cloud Connector.
*
* @return The location identifier.
*/
@Nonnull
public Optional getCloudConnectorLocationId()
{
return Optional.ofNullable(cloudConnectorLocationId);
}
@Nonnull
@Override
public Optional getTrustStore()
{
if( destinationConfiguration == null ) {
return Optional.empty();
}
return Optional.ofNullable(destinationConfiguration.getTrustStore());
}
@Nonnull
@Override
public Optional getKeyStore()
{
if( destinationConfiguration == null ) {
return Optional.empty();
}
return Optional.ofNullable(destinationConfiguration.getKeyStore());
}
private ProxyConfiguration getOnPremiseProxyConfiguration()
throws DestinationAccessException
{
final CloudPlatform cloudPlatform = CloudPlatformAccessor.getCloudPlatform();
if( !(cloudPlatform instanceof ScpNeoCloudPlatform) ) {
throw new ShouldNotHappenException(
"The current Cloud platform is not an instance of "
+ ScpNeoCloudPlatform.class.getSimpleName()
+ ". Please make sure to specify a dependency to com.sap.cloud.s4hana.cloudplatform:core-scp-neo.");
}
final ScpNeoCloudPlatform scpNeoCloudPlatform = (ScpNeoCloudPlatform) cloudPlatform;
try {
final String onPremiseProxyHost = scpNeoCloudPlatform.getOnPremiseProxyHost();
final int onPremiseProxyPort = scpNeoCloudPlatform.getOnPremiseProxyPort();
final URI uri = new URI("http://" + onPremiseProxyHost + ":" + onPremiseProxyPort);
final ProxyConfiguration proxyConfiguration = new ProxyConfiguration(uri);
if( logger.isDebugEnabled() ) {
logger.debug("Using on-premise proxy configuration: " + uri + ".");
}
return proxyConfiguration;
}
catch( final CloudPlatformException e ) {
throw new DestinationAccessException("Failed to get on-premise proxy host or port.", e);
}
catch( final URISyntaxException e ) {
throw new DestinationAccessException("Invalid on-premise proxy host or port.", e);
}
}
@Nonnull
@Override
public Optional getProxyConfiguration()
throws DestinationAccessException
{
if( getProxyType() == ProxyType.ON_PREMISE ) {
return Optional.of(getOnPremiseProxyConfiguration());
} else {
return Optional.ofNullable(proxyConfiguration);
}
}
private Collection getAuthenticationHeaders( @Nullable final URI requestUri )
throws DestinationAccessException
{
try {
return new GetAuthHeadersCommand(this, requestUri).execute();
}
catch( final HystrixRuntimeException | HystrixBadRequestException | IllegalStateException e ) {
throw new DestinationAccessException(
"Failed to get the request headers for destination '"
+ getName()
+ "' (request URI: "
+ requestUri
+ ").",
e);
}
}
@Nullable
private String getCurrentAccountId()
{
final Optional currentTenant = TenantAccessor.getCurrentTenantIfAvailable();
if( !currentTenant.isPresent() ) {
if( logger.isDebugEnabled() ) {
logger.debug(
"Unable to determine current account identifier: tenant not available. "
+ "The connectivity header '"
+ SAP_CONNECTIVITY_CONSUMER_ACCOUNT
+ "' will be omitted. "
+ "On-premise connectivity may not work in consumer accounts.");
}
return null;
}
final Tenant tenant = currentTenant.get();
if( !(tenant instanceof ScpNeoTenant) ) {
throw new ShouldNotHappenException(
"The current tenant is not an instance of "
+ ScpNeoTenant.class.getSimpleName()
+ ". Please make sure to specify a dependency to com.sap.cloud.s4hana.cloudplatform:tenant-scp-neo.");
}
return ((ScpNeoTenant) tenant).getTenantContext().getTenant().getAccount().getId();
}
private Collection getOnPremiseProxyHeaders()
{
final List headers = new ArrayList<>();
@Nullable
final String currentAccountId = getCurrentAccountId();
if( !Strings.isNullOrEmpty(currentAccountId) ) {
headers.add(new Header(SAP_CONNECTIVITY_CONSUMER_ACCOUNT, currentAccountId));
}
if( !Strings.isNullOrEmpty(cloudConnectorLocationId) ) {
headers.add(new Header(SAP_CONNECTIVITY_SCC_LOCATION_ID_HEADER, cloudConnectorLocationId));
if( logger.isDebugEnabled() ) {
logger.debug(
"Successfully added "
+ SAP_CONNECTIVITY_SCC_LOCATION_ID_HEADER
+ " header with location identifier '"
+ cloudConnectorLocationId
+ "'.");
}
}
return headers;
}
@Nonnull
@Override
public List getHeaders( @Nullable final URI requestUri )
throws DestinationAccessException
{
final List headers = super.getHeaders(requestUri);
headers.addAll(getAuthenticationHeaders(requestUri));
if( ProxyType.ON_PREMISE == getProxyType() ) {
headers.addAll(getOnPremiseProxyHeaders());
}
return headers;
}
@Nonnull
@Override
public URI getUri()
throws DestinationAccessException
{
final URI uri = super.getUri();
// for on-premise destinations, convert "https" to "http" while keeping port 443, if necessary
if( getProxyType() == ProxyType.ON_PREMISE && "https".equals(uri.getScheme()) ) {
if( logger.isWarnEnabled() ) {
logger.warn(
"Rewriting on-premise destination '"
+ getName()
+ "' with URI scheme 'https' to use 'http': "
+ uri
+ ". To resolve this warning, make sure to use 'http' for on-premise destinations.");
}
// keep the default HTTPS port if no port was specified
final int port = uri.getPort() < 0 ? DEFAULT_HTTPS_PORT : uri.getPort();
try {
return new URI(
"http",
uri.getUserInfo(),
uri.getHost(),
port,
uri.getPath(),
uri.getQuery(),
uri.getFragment());
}
catch( final URISyntaxException e ) {
throw new DestinationAccessException("Invalid destination URI.", e);
}
}
return uri;
}
}