ultrasonic-app-subsonic-and.../core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSSLTest.kt

110 lines
3.4 KiB
Kotlin

package org.moire.ultrasonic.api.subsonic
import java.io.InputStream
import java.net.InetAddress
import java.security.KeyStore
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLHandshakeException
import javax.net.ssl.TrustManagerFactory
import okhttp3.mockwebserver.MockWebServer
import org.amshove.kluent.`should throw`
import org.junit.After
import org.junit.Before
import org.junit.Test
private const val PORT = 8443
private const val HOST = "localhost"
/**
* Integration test to check [SubsonicAPIClient] interaction with different SSL scenarios.
*/
class SubsonicApiSSLTest {
private val mockWebServer = MockWebServer()
@Before
fun setUp() {
val sslContext = createSSLContext(
loadResourceStream("self-signed.pem"),
loadResourceStream("self-signed.p12"),
""
)
mockWebServer.useHttps(sslContext.socketFactory, false)
mockWebServer.start(InetAddress.getByName(HOST), PORT)
}
@After
fun tearDown() {
mockWebServer.shutdown()
}
private fun createSSLContext(
certificatePemStream: InputStream,
certificatePkcs12Stream: InputStream,
password: String
): SSLContext {
var cert: X509Certificate? = null
val trustStore = KeyStore.getInstance(KeyStore.getDefaultType())
trustStore.load(null)
certificatePemStream.use {
cert = (
CertificateFactory.getInstance("X.509")
.generateCertificate(certificatePemStream)
) as X509Certificate
}
val alias = cert?.subjectX500Principal?.name
?: throw IllegalStateException("Failed to load certificate")
trustStore.setCertificateEntry(alias, cert)
val tmf = TrustManagerFactory.getInstance("X509")
tmf.init(trustStore)
val trustManagers = tmf.trustManagers
val sslContext = SSLContext.getInstance("TLS")
val ks = KeyStore.getInstance("PKCS12")
ks.load(certificatePkcs12Stream, password.toCharArray())
val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
kmf.init(ks, password.toCharArray())
sslContext.init(kmf.keyManagers, trustManagers, null)
return sslContext
}
@Test
fun `Should fail request if self-signed certificate support is disabled`() {
val client = createSubsonicClient(false)
mockWebServer.enqueueResponse("ping_ok.json")
val fail = {
client.api.ping().execute()
}
fail `should throw` SSLHandshakeException::class
}
@Test
fun `Should pass request if self-signed certificate support is enabled`() {
val client = createSubsonicClient(true)
mockWebServer.enqueueResponse("ping_ok.json")
val response = client.api.ping().execute()
assertResponseSuccessful(response)
}
private fun createSubsonicClient(allowSelfSignedCertificate: Boolean): SubsonicAPIClient {
val config = SubsonicClientConfiguration(
"https://$HOST:$PORT/",
USERNAME,
PASSWORD,
CLIENT_VERSION,
CLIENT_ID,
allowSelfSignedCertificate = allowSelfSignedCertificate
)
return SubsonicAPIClient(config)
}
}