@@ -21,6 +21,8 @@ package org.ossreviewtoolkit.utils.ort
2121
2222import io.kotest.core.spec.style.WordSpec
2323import io.kotest.engine.spec.tempdir
24+ import io.kotest.matchers.nulls.beNull
25+ import io.kotest.matchers.nulls.shouldNotBeNull
2426import io.kotest.matchers.result.shouldBeFailure
2527import io.kotest.matchers.should
2628import io.kotest.matchers.shouldBe
@@ -34,13 +36,21 @@ import io.mockk.mockkStatic
3436import io.mockk.unmockkAll
3537
3638import java.io.IOException
39+ import java.net.HttpURLConnection
40+ import java.net.PasswordAuthentication
41+ import java.net.URI
3742import java.time.Duration
3843
44+ import kotlin.reflect.KFunction
45+
46+ import okhttp3.Authenticator as OkAuthenticator
3947import okhttp3.HttpUrl.Companion.toHttpUrl
4048import okhttp3.OkHttpClient
49+ import okhttp3.Protocol
4150import okhttp3.Request
4251import okhttp3.Response
4352import okhttp3.ResponseBody
53+ import okhttp3.Route
4454
4555import okio.BufferedSource
4656
@@ -135,4 +145,92 @@ class OkHttpClientHelperTest : WordSpec({
135145 }
136146 }
137147 }
148+
149+ " JavaNetAuthenticatorWrapper" should {
150+ " delegate to the default authenticator if the response code is not 401" {
151+ val authenticator = mockk<OkAuthenticator >()
152+ val route = mockk<Route >()
153+ val response = createResponse(HttpURLConnection .HTTP_FORBIDDEN )
154+ val modifiedRequest = mockk<Request >()
155+ every { authenticator.authenticate(route, response) } returns modifiedRequest
156+
157+ val lenientAuthenticator = JavaNetAuthenticatorWrapper (authenticator)
158+
159+ lenientAuthenticator.authenticate(route, response) shouldBe modifiedRequest
160+ }
161+
162+ " query the Java authenticator" {
163+ val response = createResponse()
164+ preparePasswordAuthentication(success = true)
165+
166+ val lenientAuthenticator = JavaNetAuthenticatorWrapper (mockk())
167+
168+ lenientAuthenticator.authenticate(mockk(), response) shouldNotBeNull {
169+ header("Authorization ") shouldBe " Basic c2NvdHQ6dGlnZXI="
170+ url shouldBe AUTH_URL .toHttpUrl()
171+ }
172+ }
173+
174+ " return null if the Java authenticator does not return a password authentication" {
175+ val response = createResponse()
176+ preparePasswordAuthentication(success = false)
177+
178+ val lenientAuthenticator = JavaNetAuthenticatorWrapper (mockk())
179+
180+ lenientAuthenticator.authenticate(mockk(), response) should beNull()
181+ }
182+
183+ " return null if the request already has an Authorization header" {
184+ val requestWithAuth = Request .Builder ()
185+ .url(AUTH_URL )
186+ .header("Authorization ", "Basic wrong-credentials")
187+ .build()
188+ val response = createResponse(originalRequest = requestWithAuth)
189+ preparePasswordAuthentication(success = true)
190+
191+ val lenientAuthenticator = JavaNetAuthenticatorWrapper (mockk())
192+
193+ lenientAuthenticator.authenticate(mockk(), response) should beNull()
194+ }
195+ }
138196})
197+
198+ private const val USERNAME = " scott"
199+ private const val PASSWORD = " tiger"
200+ private const val AUTH_URL = " https://example.org/auth"
201+
202+ /* *
203+ * Create a [Response] object with the given response [code]. The request of this response is set to [originalRequest]
204+ * if it is provided; otherwise, a default request to [AUTH_URL] is created.
205+ */
206+ private fun createResponse (
207+ code : Int = HttpURLConnection .HTTP_UNAUTHORIZED ,
208+ originalRequest : Request ? = null
209+ ): Response {
210+ val request = originalRequest ? : Request .Builder ()
211+ .url(AUTH_URL )
212+ .build()
213+ return Response .Builder ()
214+ .request(request)
215+ .code(code)
216+ .protocol(Protocol .HTTP_2 )
217+ .message(" Unauthorized" )
218+ .build()
219+ }
220+
221+ /* *
222+ * Mock the [requestPasswordAuthentication] function to return a [PasswordAuthentication] object for the test URI
223+ * based on the given [success] flag.
224+ */
225+ private fun preparePasswordAuthentication (success : Boolean ) {
226+ val result = if (success) {
227+ PasswordAuthentication (USERNAME , PASSWORD .toCharArray())
228+ } else {
229+ null
230+ }
231+
232+ val resolveFunc: (URI ) -> PasswordAuthentication ? = ::requestPasswordAuthentication
233+ mockkStatic(resolveFunc as KFunction <* >)
234+
235+ every { requestPasswordAuthentication(URI .create(AUTH_URL )) } returns result
236+ }
0 commit comments