Skip to content

Introducing Transparent Proxy Builder #851

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 11, 2025

Conversation

isevdalinosap
Copy link
Contributor

@isevdalinosap isevdalinosap commented Jul 3, 2025

Context

This PR adds support for consuming systems in Cloud SDK applications leveraging the Transparent Proxy for new destination features and on-premise connectivity.

Feature scope:

  • Implement TransparentProxyDestination class

Definition of Done

  • Functionality scope stated & covered
  • Tests cover the scope above
  • Error handling created / updated & covered by the tests above
  • Documentation updated
  • Release notes updated

Copy link

cla-assistant bot commented Jul 3, 2025

CLA assistant check
All committers have signed the CLA.

@isevdalinosap isevdalinosap force-pushed the transparentProxyBuilder branch 6 times, most recently from 7f3c14b to b4355df Compare July 3, 2025 19:55
.chainVarSubjectToken("chainVarSubjectToken")
.chainVarSubjectTokenType("chainVarSubjectTokenType")
.chainVarSamlProviderDestinationName("chainVarSamlProviderDestName")
.property(DestinationProperty.URI, "https://example.com")
Copy link
Contributor

@newtork newtork Jul 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Question)

I don't understand this. I thought destinations referenced via "Transparent Proxy" would rely on relative URIs not absolute addresses.

It is confusing to me that the following parameters are not required by API design:

  • destination name
  • namespace
  • (relative) URI

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe from transparent proxy point of view only destination name is required. The url is designed to be defaulted and the namespace is embedded into the url.

Copy link
Contributor

@newtork newtork left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For further review purpose please find the official documentation on Transparent Proxy
https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/using-transparent-proxy

Please find our current explanation in SAP Cloud SDK
https://sap.github.io/cloud-sdk/docs/java/environments/kubernetes-kyma#executing-requests

Copy link
Member

@MatKuhr MatKuhr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution!

Overall, the approach makes sense to me. I noted some open questions that require discussions. I'll create a meeting to go over them together.

Comment on lines 43 to 44
private static final String REDIRECT_URI_HEADER_KEY = "x-redirect-uri";
private static final String CODE_VERIFIER_HEADER_KEY = "x-code-verifier";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some of these headers I'm not sure what exactly they do and if or how they fit into Cloud SDK

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All those headers give different possibilities for consuming destinations using the transparent proxy.

@Override
public Collection<Header> getHeaders( @Nonnull URI requestUri )
{
return customHeaders;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to evaluate how this should interact with static header providers. Probably, those need to be loaded and evaluated.

this.customHeaders =
customHeaders != null ? ImmutableList.<Header> builder().addAll(customHeaders).build() : ImmutableList.of();

cachedProxyConfiguration = destinationPropertyFactory.getProxyConfiguration(baseProperties);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we should set the transparent proxy as a proxy in the HTTP client, or if we should leave it empty.

Copy link
Contributor Author

@isevdalinosap isevdalinosap Jul 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 111 to 127
public Option<KeyStore> getKeyStore()
{
return null;
}

@Nullable
@Override
public Option<String> getKeyStorePassword()
{
return null;
}

@Override
public boolean isTrustingAllCertificates()
{
return false;
}

@Nullable
@Override
public Option<BasicCredentials> getBasicCredentials()
{
return null;
}

@Nullable
@Override
public AuthenticationType getAuthenticationType()
{
return null;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These values will likely depend on how connections to the transparent proxy will be secured, and thus might need to be implemented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The transparent proxy is currently deployed as trusted component in trusted environment only and therefore requires no authentication/certificates. Because of this I don't think implementing those methods is needed/necessary.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's up to the user how to authenticate the connection to TP. Currently, TP is only running in K8S, where this is typically done with sidecars. So in that case, users are unlikely to need this functionality. However, TP is also coming to CF, and users may decide to authenticate differently, or want to use some of these methods for other purposes.

For now, I think it's okay to not implement actual logic, leaving these cases unsupported until users request it.

However, returning null when the method is declared Option<..> should never be done, so instead, please return Option.none() and change to @Nonnull

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed all the methods in question to return Option.none() and be annotated with @Nonnull.

* Builder class to allow for easy creation of an immutable {@code TransparentProxyDestination} instance.
*/
@Accessors( fluent = true, chain = true )
public static class Builder
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As Alex pointed out, the builder should enforce required properties

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe from transparent proxy point of view only destination name is required. The url is designed to be defaulted and the namespace is embedded into the url. I have added enforcement of the destination name.

this.property(DestinationProperty.URI, TRANSPARENT_PROXY_GATEWAY);
}

TenantAccessor.tryGetCurrentTenant().onSuccess(tenant -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have two options on how to handle tenancy:

  1. Determine the tenant upon building the destination
  2. Determine the tenant when using the destination

Currently, strategy (1) is applied, but I think (2) better fits the Cloud SDK APIs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the context of the transparent proxy all other header parameters make sense only if the tenant can be determined and basically all transparent proxy operations require tenant. So in this case constructing a TransparentProxyDestination without known tenant seems illogical to me.

@@ -0,0 +1,604 @@
package com.sap.cloud.sdk.cloudplatform.connectivity;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this functionality is still specific to the destination service, I think this would be more suitable under connectivity-destination-service module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it more appropriate for the class to reside inside com.sap.cloud.sdk.cloudplatform.connectivity because TransparentProxyDestination implements HttpDestination and is similar to class DefaultHttpDestination, both of which reside inside com.sap.cloud.sdk.cloudplatform.connectivity?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite, the modules are split based on generic API vs. destination service specific logic. Since TP conceptually only works with a destination service, it should be in the destination service module

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, moved all the logic into connectivity-destination-service module.

@isevdalinosap isevdalinosap force-pushed the transparentProxyBuilder branch 5 times, most recently from 9b14007 to 06cb15c Compare July 10, 2025 14:40
@isevdalinosap isevdalinosap force-pushed the transparentProxyBuilder branch 9 times, most recently from 59e0a6a to 54726e2 Compare July 20, 2025 20:12
Copy link
Member

@MatKuhr MatKuhr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks really good! I added some minor suggestions, mostly on test code. But I think we've covered most of the challenging bits 😉

One thing we have to keep in mind: One destination will use one HTTP client. If the tenant is added dynamically per request, the same client will be shared across all tenants.

This is important also for any endpoints that set tenant- or user-specific cookies. For such cases users should build a destination object per request and manually set auth tokens. That way, separate HTTP clients will be used.

@isevdalinosap isevdalinosap force-pushed the transparentProxyBuilder branch 4 times, most recently from 3477825 to ea07c40 Compare July 30, 2025 14:53
MatKuhr
MatKuhr previously approved these changes Aug 4, 2025
Copy link
Member

@MatKuhr MatKuhr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes LGTM!

Can you please add a release note entry here: https://github.com/SAP/cloud-sdk-java/blob/main/release_notes.md?plain=1#L13

it should briefly state the new feature and link to the docs page (even if the docs page doesn't exist yet, please put the expected URL)

@MatKuhr MatKuhr enabled auto-merge (squash) August 11, 2025 13:55
@MatKuhr MatKuhr merged commit 18a98eb into SAP:main Aug 11, 2025
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants