Skip to content
Closed
Changes from 2 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
e4ea2d5
Changes to add proxy url in WIC
anannya03 Oct 10, 2025
da9e1d2
Merge branch 'main' into fic_limit_add_proxy
anannya03 Oct 10, 2025
4cc6934
Apply suggestion from @Copilot
anannya03 Oct 10, 2025
32ae943
update conditions in detect-api-changes.yml (#46971)
azure-sdk Oct 10, 2025
f3be809
[AutoPR azure-resourcemanager-oracledatabase]-generated-from-SDK Gene…
azure-sdk Oct 11, 2025
d15d8ce
mgmt, local generation for Container Service - 2025-08-01 (#46948)
v-hongli1 Oct 13, 2025
a793987
mgmt, prepare containerservice 2.55.0 (#46981)
XiaofeiCao Oct 13, 2025
f0d0b27
mgmt, local generation for Microsoft.Network 2024-10-01 (#46945)
v-hongli1 Oct 13, 2025
51b44c2
Increment package versions for containerservice releases (#46982)
azure-sdk Oct 13, 2025
8f80a14
mgmt network, fix availabilityzones and prepare release 2.54.0 (#46966)
XiaofeiCao Oct 13, 2025
045894d
Sync eng/common directory with azure-sdk-tools for PR 12454 (#46974)
azure-sdk Oct 13, 2025
0793e0b
Sync eng/common directory with azure-sdk-tools for PR 12410 (#46928)
azure-sdk Oct 13, 2025
079701f
Update main with branch release of azure-search-documents 11.8.0 (#46…
alzimmermsft Oct 13, 2025
476fc56
Configurations: 'specification/codesigning/CodeSigning.Management/ts…
azure-sdk Oct 13, 2025
2d594a9
Use write-output for matrix script to support upstream callers (#46989)
azure-sdk Oct 13, 2025
388e64d
Bump @azure-tools/typespec-client-generator-cli (#46991)
azure-sdk Oct 13, 2025
16b05b4
Add OpenRewrite recipes for appconfiguration to automate migration to…
DoggyDoggyDoggy Oct 14, 2025
dafdc57
update release date (#46986)
IannGeorges Oct 14, 2025
cc92d35
Increment versions for network releases (#46984)
azure-sdk Oct 14, 2025
8efbfc2
Increment package versions for storagemover releases (#46996)
azure-sdk Oct 14, 2025
05d1519
Support stable database semconv (#46957)
trask Oct 14, 2025
263a2fc
Increment Versions for Identity Release 1.18.1 (#46994)
anannya03 Oct 14, 2025
553673c
Sync eng/common directory with azure-sdk-tools for PR 12467 (#46999)
azure-sdk Oct 14, 2025
03db57b
[Automation] Generate Fluent Lite from Swagger netapp#package-preview…
azure-sdk Oct 15, 2025
e8831ab
eng, automation, update changelog tool, to include item on class cont…
weidongxu-microsoft Oct 15, 2025
625a975
Increment package versions for netapp releases (#47008)
azure-sdk Oct 15, 2025
a902dcf
Increment package versions for oracledatabase releases (#47010)
azure-sdk Oct 15, 2025
66fdb6d
[Automation] Generate Fluent Lite from Swagger hdinsight#package-2025…
azure-sdk Oct 15, 2025
1725960
release for aks package-preview-2025-08 (#47006)
weidongxu-microsoft Oct 15, 2025
f3ef65d
Increment package versions for dataprotection releases (#47011)
azure-sdk Oct 15, 2025
9b86363
workaround for computefleet test failure (#47009)
weidongxu-microsoft Oct 15, 2025
b9a9305
Increment package versions for hdinsight releases (#47012)
azure-sdk Oct 15, 2025
25e06a5
Increment package versions for containerservice releases (#47015)
azure-sdk Oct 15, 2025
ae038cb
[Automation] Generate Fluent Lite from Swagger specification/redisent…
azure-sdk Oct 15, 2025
0f93a2c
Update to Java 25 as matrix upper bound (#46967)
alzimmermsft Oct 15, 2025
979bebe
CFP Consistent TS Format (#46784)
tvaron3 Oct 15, 2025
2a11c6e
Sync eng/common directory with azure-sdk-tools for PR 12483 (#47000)
azure-sdk Oct 15, 2025
4272c25
Sync eng/common directory with azure-sdk-tools for PR 12501 (#47020)
azure-sdk Oct 15, 2025
ee88ee0
[AutoPR azure-resourcemanager-dashboard]-generated-from-SDK Generatio…
azure-sdk Oct 15, 2025
3c5a1f4
Avoid throwing `NullPointerException` from Per-Partition Circuit Brea…
jeet1995 Oct 15, 2025
f2895ac
Increment package versions for redisenterprise releases (#47019)
azure-sdk Oct 16, 2025
0086bcb
Prevent reducing network request timeout to less than 5s for Direct M…
jeet1995 Oct 16, 2025
b948dfb
Remove RemoteRendering SDK due to sunset (#46907)
MichaelZp0 Oct 16, 2025
3d5e17a
Spark-4 Preparation: Adding CI live test for DBX runtime 16.4 (Spark …
FabianMeiswinkel Oct 16, 2025
f0e26f2
Changes to add proxy url in WIC
anannya03 Oct 10, 2025
e57e945
Merge branch 'fic_limit_add_proxy' of https://github.com/anannya03/az…
anannya03 Oct 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
package com.azure.identity.implementation;

import com.azure.core.http.HttpClient;
import com.azure.core.http.HttpPipelineCallContext;
import com.azure.core.http.HttpPipelineNextPolicy;
import com.azure.core.http.HttpPipelineNextSyncPolicy;
import com.azure.core.http.HttpRequest;
import com.azure.core.http.HttpResponse;
import com.azure.core.http.policy.HttpPipelinePolicy;
import com.azure.core.util.Configuration;
import com.azure.core.util.CoreUtils;
import com.azure.core.util.logging.ClientLogger;

import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

import reactor.core.publisher.Mono;


public class WorkloadIdentityTokenProxyPolicy implements HttpPipelinePolicy {

private static final ClientLogger LOGGER = new ClientLogger(WorkloadIdentityCustomProxyConfiguration.class);

public static final String AZURE_KUBERNETES_TOKEN_PROXY = "AZURE_KUBERNETES_TOKEN_PROXY";
public static final String AZURE_KUBERNETES_CA_FILE = "AZURE_KUBERNETES_CA_FILE";
public static final String AZURE_KUBERNETES_CA_DATA = "AZURE_KUBERNETES_CA_DATA";
public static final String AZURE_KUBERNETES_SNI_NAME = "AZURE_KUBERNETES_SNI_NAME";

private byte[] caData;
private String caFile;
private String sniName;
private URI tokenProxyUri;
private HttpClient httpClient;
private SSLContext sslContext;
private byte[] lastCaBytes;


WorkloadIdentityTokenProxyPolicy(IdentityClientOptions identityClientOptions) {
Configuration configuration = identityClientOptions.getConfiguration() == null
? Configuration.getGlobalConfiguration()
: identityClientOptions.getConfiguration();

String tokenProxyUrl = configuration.get(AZURE_KUBERNETES_TOKEN_PROXY);
String sniName = configuration.get(AZURE_KUBERNETES_SNI_NAME);
String caFile = configuration.get(AZURE_KUBERNETES_CA_FILE);
String caData = configuration.get(AZURE_KUBERNETES_CA_DATA);

if (CoreUtils.isNullOrEmpty(tokenProxyUrl)) {
if (!CoreUtils.isNullOrEmpty(sniName)
|| !CoreUtils.isNullOrEmpty(caFile)
|| !CoreUtils.isNullOrEmpty(caData)) {
throw LOGGER.logExceptionAsError(new IllegalStateException(
"AZURE_KUBERNETES_TOKEN_PROXY is not set but other custom endpoint-related environment variables are present"));
}
this.tokenProxyUri = null;
this.caFile = null;
this.caData = null;
this.sniName = null;
return;
}

if (!CoreUtils.isNullOrEmpty(caFile) && !CoreUtils.isNullOrEmpty(caData)) {
throw LOGGER.logExceptionAsError(new IllegalStateException(
"Only one of AZURE_KUBERNETES_CA_FILE or AZURE_KUBERNETES_CA_DATA can be set."));
}

this.tokenProxyUri = parseAndValidateProxyUrl(tokenProxyUrl);
this.sniName = sniName;
this.caFile = caFile;
this.caData = CoreUtils.isNullOrEmpty(caData) ? null : caData.getBytes();
}

@Override
public Mono<HttpResponse> process(HttpPipelineCallContext context, HttpPipelineNextPolicy next) {
//HttpClient client = createHttpClient();
HttpRequest request = context.getHttpRequest();
HttpRequest proxyRequest = rewriteTokenRequestForProxy(request);
context.setHttpRequest(proxyRequest);
return next.process();
}


@Override
public HttpResponse processSync(HttpPipelineCallContext context, HttpPipelineNextSyncPolicy next) {
HttpRequest request = context.getHttpRequest();
HttpRequest proxyRequest = rewriteTokenRequestForProxy(request);
context.setHttpRequest(proxyRequest);
return next.processSync();
}

private HttpRequest rewriteTokenRequestForProxy(HttpRequest request) {
try {
URI originalUri = request.getUrl().toURI();
String originalPath = originalUri.getRawPath();
String originalQuery = originalUri.getRawQuery();

String tokenProxyBase = tokenProxyUri.toString();
if(!tokenProxyBase.endsWith("/")) tokenProxyBase += "/";
Copy link

Copilot AI Oct 10, 2025

Choose a reason for hiding this comment

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

Missing space after 'if' keyword. Should follow Java coding conventions with proper spacing.

Suggested change
if(!tokenProxyBase.endsWith("/")) tokenProxyBase += "/";
if (!tokenProxyBase.endsWith("/")) tokenProxyBase += "/";

Copilot uses AI. Check for mistakes.
URI combined = URI.create(tokenProxyBase).resolve(originalPath.startsWith("/") ? originalPath.substring(1) : originalPath);

String combinedStr = combined.toString();
if (originalQuery != null && !originalQuery.isEmpty()) {
combinedStr += "?" + originalQuery;
}

URI newUri = URI.create(combinedStr);
HttpRequest newRequest = new HttpRequest(request.getHttpMethod(), newUri.toURL());

if (request.getHeaders() != null) {
newRequest.setHeaders(request.getHeaders());
}

if (request.getBodyAsBinaryData() != null) {
newRequest.setBody(request.getBodyAsBinaryData());
}

return newRequest;

} catch (Exception e) {
throw new RuntimeException("Failed to rewrite token request for proxy", e);
}
}

// private HttpClient createHttpClient() {
// if((caData == null || caData.length == 0) && (caFile == null || caFile.isEmpty())) {
// if(httpClient == null) {
// // httpClient =
// }
// }
// if(caFile == null || caFile.isEmpty()) {
// // httpClient =
// }
// throw new UnsupportedOperationException("Unimplemented method 'createHttpClient'");
// }

Comment on lines +137 to +148
Copy link

Copilot AI Oct 10, 2025

Choose a reason for hiding this comment

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

Commented-out code should be removed before merging. This appears to be unused development code that clutters the implementation.

Suggested change
// private HttpClient createHttpClient() {
// if((caData == null || caData.length == 0) && (caFile == null || caFile.isEmpty())) {
// if(httpClient == null) {
// // httpClient =
// }
// }
// if(caFile == null || caFile.isEmpty()) {
// // httpClient =
// }
// throw new UnsupportedOperationException("Unimplemented method 'createHttpClient'");
// }

Copilot uses AI. Check for mistakes.
private SSLContext getSSLContext() {
try {
// If no CA override provide, use default
Copy link

Copilot AI Oct 10, 2025

Choose a reason for hiding this comment

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

Grammar error in comment. Should be 'If no CA override provided, use default' or 'If no CA override is provided, use default'.

Suggested change
// If no CA override provide, use default
// If no CA override provided, use default

Copilot uses AI. Check for mistakes.
if(CoreUtils.isNullOrEmpty(caFile) && (caData == null || caData.length == 0)) {
if(sslContext == null) {
sslContext = SSLContext.getDefault();
}
return sslContext;
}

// If CA data provided, use it
if(CoreUtils.isNullOrEmpty(caFile)) {
if(sslContext == null) {
sslContext = createSslContextFromBytes(caData);
}
return sslContext;
}

// If CA file provided, read it (and re-read if it changes)
Path path = Paths.get(caFile);
if(!Files.exists(path)) {
Copy link

Copilot AI Oct 10, 2025

Choose a reason for hiding this comment

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

Missing space after 'if' keyword. Should follow Java coding conventions with proper spacing.

Suggested change
if(!Files.exists(path)) {
if (!Files.exists(path)) {

Copilot uses AI. Check for mistakes.
throw new IOException("CA File not found: " + caFile);
}

byte[] currentContent = Files.readAllBytes(path);

if(currentContent.length == 0) {
Copy link

Copilot AI Oct 10, 2025

Choose a reason for hiding this comment

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

Missing space after 'if' keyword. Should follow Java coding conventions with proper spacing.

Suggested change
if(currentContent.length == 0) {
if (currentContent.length == 0) {

Copilot uses AI. Check for mistakes.
if(sslContext == null) {
throw new IOException("CA File " + caFile + " is empty.");
}
return sslContext;
}

if(sslContext == null || !Arrays.equals(currentContent, lastCaBytes)) {
Copy link

Copilot AI Oct 10, 2025

Choose a reason for hiding this comment

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

Missing space after 'if' keyword. Should follow Java coding conventions with proper spacing.

Copilot uses AI. Check for mistakes.
sslContext = createSslContextFromBytes(currentContent);
lastCaBytes = currentContent;
}

return sslContext;

} catch (Exception e) {
throw new RuntimeException("Failed to create default SSLContext", e);
}
}

// Create SSLContext from byte array containing PEM certificate data
private SSLContext createSslContextFromBytes(byte[] certificateData) {
try (InputStream inputStream = new ByteArrayInputStream(certificateData)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate caCert = (X509Certificate) cf.generateCertificate(inputStream);
return createSslContext(caCert);
} catch (Exception e) {
throw new RuntimeException("Failed to create SSLContext from bytes", e);
}
}

// Create SSLContext from a single X509Certificate
private SSLContext createSslContext(X509Certificate caCert) {
try {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(null, null);
keystore.setCertificateEntry("ca-cert", caCert);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keystore);

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
return context;
} catch (Exception e) {
throw new RuntimeException("Failed to create SSLContext", e);
}
}

/**
* Parses and validates the custom token proxy URL.
*
* @param endpoint The proxy endpoint URL string
* @return Validated URI
* @throws IllegalArgumentException if URL is invalid
*/
private static URI parseAndValidateProxyUrl(String endpoint) {
if (CoreUtils.isNullOrEmpty(endpoint)) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException("Proxy endpoint cannot be null or empty"));
}

URI tokenProxy;
try {
tokenProxy = new URI(endpoint);
} catch (URISyntaxException e) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException(
"Failed to parse custom token proxy URL: " + endpoint, e));
}

if (!"https".equals(tokenProxy.getScheme())) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException(
"Custom token endpoint must use https scheme, got: " + tokenProxy.getScheme()));
}

if (tokenProxy.getRawUserInfo() != null) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException(
"Custom token endpoint URL must not contain user info: " + endpoint));
}

if (tokenProxy.getRawQuery() != null) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException(
"Custom token endpoint URL must not contain a query: " + endpoint));
}

if (tokenProxy.getRawFragment() != null) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException(
"Custom token endpoint URL must not contain a fragment: " + endpoint));
}

if (tokenProxy.getRawPath() == null || tokenProxy.getRawPath().isEmpty()) {
try {
tokenProxy = new URI(tokenProxy.getScheme(), null, tokenProxy.getHost(),
tokenProxy.getPort(), "/", null, null);
} catch (URISyntaxException e) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException(
"Failed to normalize proxy URL path", e));
}
}

return tokenProxy;
}

}