Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 14 additions & 5 deletions sdk-core/src/main/kotlin/org/dexpace/sdk/core/util/ProxyOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,23 @@ import java.util.regex.Pattern
*
* ## Proxy authentication
*
* Proxy auth is driven by [username] / [password]. The shipped transports apply them as follows:
* - OkHttp transport: sets up **Basic** proxy authentication from [username] / [password].
* - JDK transport: passes [username] / [password] to the `java.net.http` stack, which negotiates
* **Basic** or **Digest** with the proxy itself.
* Proxy auth is driven by [username] / [password]. **Both shipped transports authenticate the
* proxy with the Basic scheme only:**
* - OkHttp transport: its `proxyAuthenticator` emits `Proxy-Authorization: Basic …` from
* [username] / [password].
* - JDK transport: installs a `java.net.Authenticator` on the `java.net.http` client. That
* built-in integration answers Basic proxy challenges only; it does not implement Digest
* proxy auth.
*
* Neither transport performs Digest (or any other non-Basic scheme) proxy authentication. To
* authenticate against a Digest-only proxy, supply your own pre-configured client — a
* `java.net.http.HttpClient` or `OkHttpClient` carrying your own authenticator — through the
* transport's `create(...)` entry point; the SDK uses such a client as-is and does not override
* its proxy authentication.
*
* [challengeHandler] is **currently not honoured by any shipped transport** — it is reserved for
* a future pluggable proxy-auth mechanism. Setting it has no effect today (the transports ignore
* it and log a warning), so supply [username] / [password] for proxy authentication.
* it and log a warning), so supply [username] / [password] for Basic proxy authentication.
*
* ## Bypass-all semantics (breaking change from pre-v2 API)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,14 +376,18 @@ public class JdkHttpTransport private constructor(
* [ProxyAuthenticator]. The JDK client picks up this authenticator at request
* time; it answers **only** proxy (407) challenges whose host/port match the
* configured proxy, returning `null` for origin-server (401) challenges so the
* proxy credentials never leak to an origin host.
* proxy credentials never leak to an origin host. The `java.net.http` client's
* built-in handling of a registered `Authenticator` covers the **Basic** scheme
* only — this transport does **not** perform Digest proxy authentication.
*
* A configured [ProxyOptions.challengeHandler] is **not** honoured by this transport:
* `java.net.http.HttpClient` exposes no per-407 hook through which a custom
* `ChallengeHandler` (e.g. Digest) could be invoked, so the handler is dropped with a
* loud warning rather than silently ignored. Proxy authentication falls back to the
* JDK's own username/password negotiation via [ProxyAuthenticator]. Consumers needing
* Digest proxy auth should use the OkHttp transport.
* loud warning rather than silently ignored. Proxy authentication falls back to Basic
* auth derived from [ProxyOptions.username] / [ProxyOptions.password] via
* [ProxyAuthenticator]. To authenticate against a Digest-only proxy, pass a
* pre-configured `java.net.http.HttpClient` carrying your own `Authenticator` to
* [create]; the SDK uses that client as-is.
*
* Credentials are deliberately never logged.
*/
Expand All @@ -397,8 +401,9 @@ public class JdkHttpTransport private constructor(
.log(
"ProxyOptions.challengeHandler is set but the JDK transport cannot invoke a " +
"custom ChallengeHandler: java.net.http.HttpClient exposes no per-407 hook. " +
"The handler is ignored; proxy auth falls back to username/password via the " +
"JDK's own auth negotiation. Use the OkHttp transport for Digest proxy auth.",
"The handler is ignored; proxy auth falls back to Basic auth derived from " +
"ProxyOptions.username / ProxyOptions.password. For Digest proxy auth, pass a " +
"pre-configured java.net.http.HttpClient with your own Authenticator to create().",
)
}
if (options.type != ProxyOptions.Type.HTTP) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,31 @@ class ProxyAuthenticatorTest {
assertNull(auth, "a proxy challenge on a non-configured port must not receive credentials")
}

/**
* Pins the documented proxy-auth contract: the credentials this authenticator returns are
* the raw username/password, with no scheme negotiation of its own. Whether a challenge is
* actually satisfied is decided by the `java.net.http` client's built-in handling of a
* registered `Authenticator`, which covers the **Basic** scheme only — it does not drive
* Digest proxy auth through this hook. The authenticator therefore returns the same
* credentials whether the proxy advertises `Basic` or `Digest`; a Digest-only proxy is not
* authenticated end-to-end, matching the [JdkHttpTransport.Builder] KDoc.
*/
@Test
fun `proxy challenge credentials carry no scheme of their own`() {
val proxy = Authenticator.RequestorType.PROXY
val basic = challenge(host = "proxy.example", port = 3128, type = proxy, scheme = "Basic")
val digest = challenge(host = "proxy.example", port = 3128, type = proxy, scheme = "Digest")
requireNotNull(basic) { "Basic proxy challenge must be answered" }
requireNotNull(digest) { "the authenticator does not inspect the scheme string" }
assertEquals(basic.userName, digest.userName)
assertEquals(String(basic.password), String(digest.password))
}

private fun challenge(
host: String,
port: Int,
type: Authenticator.RequestorType,
scheme: String = "Basic",
): PasswordAuthentication? =
Authenticator.requestPasswordAuthentication(
authenticator,
Expand All @@ -73,7 +94,7 @@ class ProxyAuthenticatorTest {
port,
"http",
"challenge",
"Basic",
scheme,
URL("http://$host:$port/"),
type,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,8 @@ public class OkHttpTransport private constructor(
"The OkHttp transport does not honour ProxyOptions.challengeHandler; it is " +
"ignored. Proxy authentication falls back to Basic auth derived from " +
"ProxyOptions.username / ProxyOptions.password. Supply those credentials, " +
"or use a transport that supports a custom proxy challenge handler.",
"or for Digest proxy auth pass a pre-configured OkHttpClient with your own " +
"proxyAuthenticator to create().",
)
}
val javaType =
Expand Down
Loading