feat(android): add a repository for extracting CSRF token

This commit is contained in:
Harsh Shandilya 2023-03-10 01:28:53 +05:30
parent 93d2397572
commit c1f1d67bfa
No known key found for this signature in database
7 changed files with 111 additions and 3 deletions

View file

@ -0,0 +1,36 @@
/*
* Copyright © 2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.android.viewmodel
import dev.msfjarvis.claw.android.injection.IODispatcher
import dev.msfjarvis.claw.api.injection.BaseUrl
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.Jsoup
/** Helper for extracting CSRF token for authenticated requests to https://lobste.rs. */
class CSRFRepository
@Inject
constructor(
private val okHttpClient: OkHttpClient,
@IODispatcher private val dispatcher: CoroutineDispatcher,
@BaseUrl private val url: String,
) {
suspend fun extractToken(): String? {
val request = Request.Builder().url(url).get().build()
return withContext(dispatcher) {
okHttpClient.newCall(request).execute().use { response ->
val doc = Jsoup.parse(response.body?.string() ?: return@use null)
val element = doc.select("meta[name=\"csrf-token\"]").first() ?: return@use null
return@use element.attr("content")
}
}
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright © 2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.android.viewmodel
import io.kotest.core.spec.Spec
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.Dispatchers
import okhttp3.OkHttpClient
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.RecordedRequest
class CSRFRepositoryTest : FunSpec() {
private val server = MockWebServer()
init {
test("Correctly extracts CSRF token").config(coroutineTestScope = true) {
val repo =
CSRFRepository(
OkHttpClient.Builder().build(),
Dispatchers.Default,
server.url("/").toString(),
)
repo.extractToken() shouldBe
"OZWykgFemPVeOSNmB53-ccKXe458X7xCInO1-qzFU6nk_9RCSrSQqS9OPmA5_pyy8qD3IYAIZ7XfAM3gdhJpkQ"
}
}
override suspend fun beforeSpec(spec: Spec) {
super.beforeSpec(spec)
val dispatcher =
object : Dispatcher() {
override fun dispatch(request: RecordedRequest): MockResponse {
return when (val path = request.path) {
"/" ->
MockResponse()
.setResponseCode(200)
.setBody(
javaClass.classLoader!!
.getResourceAsStream("csrf_page.html")
.readAllBytes()
.decodeToString(),
)
else -> error("Invalid path: $path")
}
}
}
server.dispatcher = dispatcher
}
}

File diff suppressed because one or more lines are too long