Skip to content
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

KTOR-1464 Fix client Digest auth realm handling #2347

Merged
merged 1 commit into from
Apr 29, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public class Auth(

val request = HttpRequestBuilder()
request.takeFromWithExecutionContext(context)
request.attributes.put(AuthHeaderAttribute, authHeader)
provider.addRequestHeaders(request)
request.attributes.put(circuitBreaker, Unit)

Expand All @@ -67,3 +68,11 @@ public class Auth(
public fun HttpClientConfig<*>.Auth(block: Auth.() -> Unit) {
install(Auth, block)
}

/**
* AuthHeader from the previous unsuccessful request. This actually should be passed as
* parameter to AuthProvider.addRequestHeaders instead in the future and the attribute will
* be removed after that.
*/
@PublicAPICandidate("1.6.0")
internal val AuthHeaderAttribute = AttributeKey<HttpAuthHeader>("AuthHeader")
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,18 @@ public class DigestAuthProvider(

override fun isApplicable(auth: HttpAuthHeader): Boolean {
if (auth !is HttpAuthHeader.Parameterized ||
auth.parameter("realm") != realm ||
auth.authScheme != AuthScheme.Digest
) return false

val newNonce = auth.parameter("nonce") ?: return false
val newQop = auth.parameter("qop")
val newOpaque = auth.parameter("opaque")

val newRealm = auth.parameter("realm") ?: return false
if (newRealm != realm && realm != null) {
return false
}

serverNonce.value = newNonce
qop.value = newQop
opaque.value = newOpaque
Expand Down Expand Up @@ -90,6 +94,9 @@ public class DigestAuthProvider(
}

val token = makeDigest(tokenSequence.joinToString(":"))
val realm = realm ?: request.attributes.getOrNull(AuthHeaderAttribute)?.let { auth ->
(auth as? HttpAuthHeader.Parameterized)?.parameter("realm")
}

val auth = HttpAuthHeader.Parameterized(
AuthScheme.Digest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,32 @@ class DigestProviderTest {
checkStandardFields(authHeader)
}

@Test
fun addRequestHeadersMissingRealm() = testSuspend {
if (!PlatformUtils.IS_JVM) return@testSuspend

val providerWithoutRealm = DigestAuthProvider("username", "pass", null)
val authHeader = parseAuthorizationHeader(authAllFields)!!
requestBuilder.attributes.put(AuthHeaderAttribute, authHeader)

assertTrue(providerWithoutRealm.isApplicable(authHeader))
providerWithoutRealm.addRequestHeaders(requestBuilder)

val resultAuthHeader = requestBuilder.headers[HttpHeaders.Authorization]!!
checkStandardFields(resultAuthHeader)
}

@Test
fun addRequestHeadersChangedRealm() = testSuspend {
if (!PlatformUtils.IS_JVM) return@testSuspend

val providerWithoutRealm = DigestAuthProvider("username", "pass", "wrong!")
val authHeader = parseAuthorizationHeader(authAllFields)!!
requestBuilder.attributes.put(AuthHeaderAttribute, authHeader)

assertFalse(providerWithoutRealm.isApplicable(authHeader))
}

@Test
fun addRequestHeadersOmitsQopAndOpaqueWhenMissing() = testSuspend {
if (!PlatformUtils.IS_JVM) return@testSuspend
Expand Down