mirror of
https://github.com/chmouel/liseur.git
synced 2026-10-14 03:39:09 +00:00
fix: Keep BookOrbit sign-in details on the server they belong to
Some checks are pending
Build / build (push) Waiting to run
Some checks are pending
Build / build (push) Waiting to run
When a BookOrbit server answered a request by pointing somewhere else, the app followed it and could send the reader's session, and sometimes what was being saved, to an address it had never checked. Only saving a reading place or status was protected. Every BookOrbit request now refuses to be pointed elsewhere.
This commit is contained in:
parent
5a797265b6
commit
dbcde0bf13
2 changed files with 35 additions and 8 deletions
|
|
@ -32,8 +32,18 @@ import org.json.JSONObject
|
|||
*/
|
||||
class BookOrbitHttp(
|
||||
private val session: BookOrbitSession,
|
||||
private val http: RemoteHttp = RemoteHttp(),
|
||||
http: RemoteHttp = RemoteHttp(),
|
||||
) {
|
||||
/**
|
||||
* Never follows a redirect: the bearer and any request body would go
|
||||
* wherever the server pointed, without [BookOrbitRequestContext.covers]
|
||||
* seeing the new address.
|
||||
*/
|
||||
private val client = http.client.newBuilder()
|
||||
.followRedirects(false)
|
||||
.followSslRedirects(false)
|
||||
.build()
|
||||
|
||||
/** A mutation must not be replayed merely because its response was lost. */
|
||||
sealed interface MutationResult {
|
||||
data object ReadBackRequired : MutationResult
|
||||
|
|
@ -72,9 +82,7 @@ class BookOrbitHttp(
|
|||
val request = signed(context, url, bearer)
|
||||
.method(method, body).build()
|
||||
try {
|
||||
http.client.newBuilder()
|
||||
.followRedirects(false)
|
||||
.followSslRedirects(false)
|
||||
client.newBuilder()
|
||||
.retryOnConnectionFailure(false)
|
||||
.build().newCall(request).execute().use { response ->
|
||||
when {
|
||||
|
|
@ -111,7 +119,7 @@ class BookOrbitHttp(
|
|||
url: String,
|
||||
): JSONObject? = withContext(Dispatchers.IO) {
|
||||
session.authorized(context) { bearer ->
|
||||
http.client.newCall(signed(context, url, bearer).build()).execute().use { response ->
|
||||
client.newCall(signed(context, url, bearer).build()).execute().use { response ->
|
||||
if (!response.isSuccessful) throw RemoteHttpFailure(failureForCode(response.code))
|
||||
val text = response.body?.string().orEmpty()
|
||||
if (text.isBlank()) null else asObject(text)
|
||||
|
|
@ -127,7 +135,7 @@ class BookOrbitHttp(
|
|||
): JSONObject? = withContext(Dispatchers.IO) {
|
||||
session.authorized(context) { bearer ->
|
||||
val request = signed(context, url, bearer).post(json.toString().toRequestBody(JSON)).build()
|
||||
http.client.newCall(request).execute().use { response ->
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (response.code in rejected) return@authorized null
|
||||
asObject(body(response))
|
||||
}
|
||||
|
|
@ -154,7 +162,7 @@ class BookOrbitHttp(
|
|||
val request = signed(context, url, bearer)
|
||||
.method(method, if (method == "DELETE" && json == null) null else body)
|
||||
.build()
|
||||
http.client.newCall(request).execute().use { response ->
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (response.isSuccessful || response.code in rejected) return@authorized response.code
|
||||
throw RemoteHttpFailure(failureForCode(response.code))
|
||||
}
|
||||
|
|
@ -166,7 +174,7 @@ class BookOrbitHttp(
|
|||
url: String,
|
||||
): String = withContext(Dispatchers.IO) {
|
||||
session.authorized(context) { bearer ->
|
||||
body(http.client.newCall(signed(context, url, bearer).build()).execute())
|
||||
body(client.newCall(signed(context, url, bearer).build()).execute())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -100,6 +100,25 @@ class BookOrbitCatalogClientTest {
|
|||
{"id":$fileId,"format":"epub","role":"$role","sizeBytes":1925282}]}
|
||||
""".trimIndent()
|
||||
|
||||
@Test
|
||||
fun `an API call does not follow a redirect with the bearer or body`() = runBlocking {
|
||||
val dao = FakeServerDao(account())
|
||||
val http = BookOrbitHttp(BookOrbitSession(dao, RemoteHttp(), now = { 1_000L }))
|
||||
val context = BookOrbitRequestContext.from(account())!!
|
||||
repeat(2) {
|
||||
server.enqueue(MockResponse(code = 307, headers = okhttp3.Headers.headersOf("Location", "/elsewhere")))
|
||||
}
|
||||
|
||||
val get = runCatching { http.getObject(context, "${address()}/api/v1/books/1") }
|
||||
val post = runCatching {
|
||||
http.postObject(context, "${address()}/api/v1/books/query", org.json.JSONObject().put("q", "x"))
|
||||
}
|
||||
|
||||
assertTrue(get.exceptionOrNull() is com.chmouel.liseur.data.remote.RemoteHttpFailure)
|
||||
assertTrue(post.exceptionOrNull() is com.chmouel.liseur.data.remote.RemoteHttpFailure)
|
||||
assertEquals(2, server.requestCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a book with no epub is not put on the shelf`() = runBlocking {
|
||||
val dao = FakeServerDao(account())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue