diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/files/FileManager.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/files/FileManager.kt index 1c8453b8516351a225252ef6f31941bc5acb41ad..77e9ac4fbf2d91e2dc38ccf50a5d2c2ebeba0412 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/files/FileManager.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/files/FileManager.kt @@ -3,8 +3,10 @@ package org.briarproject.mailbox.core.files import io.ktor.application.ApplicationCall import io.ktor.auth.principal import io.ktor.http.HttpStatusCode +import io.ktor.request.receiveStream import io.ktor.response.respond -import org.briarproject.mailbox.core.db.Database +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import org.briarproject.mailbox.core.server.AuthException import org.briarproject.mailbox.core.server.AuthManager import org.briarproject.mailbox.core.server.MailboxPrincipal @@ -13,8 +15,8 @@ import org.briarproject.mailbox.core.system.RandomIdManager import javax.inject.Inject class FileManager @Inject constructor( - private val db: Database, private val authManager: AuthManager, + private val fileProvider: FileProvider, private val randomIdManager: RandomIdManager, ) { @@ -32,9 +34,19 @@ class FileManager @Inject constructor( randomIdManager.assertIsRandomId(folderId) authManager.assertCanPostToFolder(principal, folderId) - // TODO implement - - call.respond(HttpStatusCode.OK, "post: Not yet implemented. folderId: $folderId}") + val fileId = randomIdManager.getNewRandomId() + withContext(Dispatchers.IO) { + val tmpFile = fileProvider.getTemporaryFile(fileId) + tmpFile.outputStream().use { outputStream -> + call.receiveStream().use { inputStream -> + inputStream.copyTo(outputStream) + } + } + val file = fileProvider.getFile(folderId, fileId) + if (!tmpFile.renameTo(file)) error("Error moving file") + } + + call.respond(HttpStatusCode.OK) } /** diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/files/FileManagerIntegrationTest.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/files/FileManagerIntegrationTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..1b72883219e6b6a88c9808c0444976e8344ad8a2 --- /dev/null +++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/files/FileManagerIntegrationTest.kt @@ -0,0 +1,66 @@ +package org.briarproject.mailbox.core.files + +import io.ktor.client.request.post +import io.ktor.client.statement.HttpResponse +import io.ktor.client.statement.readText +import io.ktor.http.HttpStatusCode +import kotlinx.coroutines.runBlocking +import org.briarproject.mailbox.core.TestUtils.getNewRandomId +import org.briarproject.mailbox.core.server.IntegrationTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle +import kotlin.random.Random +import kotlin.test.assertEquals + +@TestInstance(Lifecycle.PER_CLASS) +class FileManagerIntegrationTest : IntegrationTest() { + + private val bytes = Random.nextBytes(2048) + + override fun initDb() { + addOwnerToken() + addContact(contact1) + addContact(contact2) + } + + @Test + fun `post new file rejects wrong token`(): Unit = runBlocking { + val response: HttpResponse = httpClient.post("$baseUrl/files/${getNewRandomId()}") { + authenticateWithToken(token) + body = bytes + } + assertEquals(HttpStatusCode.Unauthorized.value, response.status.value) + } + + @Test + fun `post new file rejects unauthorized folder ID`(): Unit = runBlocking { + val response: HttpResponse = httpClient.post("$baseUrl/files/${contact1.outboxId}") { + authenticateWithToken(ownerToken) + body = bytes + } + assertEquals(HttpStatusCode.Unauthorized.value, response.status.value) + } + + @Test + fun `post new file rejects invalid folder ID`(): Unit = runBlocking { + val response: HttpResponse = httpClient.post("$baseUrl/files/foo") { + authenticateWithToken(ownerToken) + body = bytes + } + assertEquals(HttpStatusCode.BadRequest.value, response.status.value) + assertEquals("Malformed ID: foo", response.readText()) + } + + @Test + fun `post new file creates new file`(): Unit = runBlocking { + val response: HttpResponse = httpClient.post("$baseUrl/files/${contact1.inboxId}") { + authenticateWithToken(ownerToken) + body = bytes + } + assertEquals(HttpStatusCode.OK.value, response.status.value) + + // TODO fetch the file later to see that it was uploaded correctly + } + +} diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/files/FileManagerTest.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/files/FileManagerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..0d21736ef398bdf42db5790546a4a2b17c04bfa6 --- /dev/null +++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/files/FileManagerTest.kt @@ -0,0 +1,65 @@ +package org.briarproject.mailbox.core.files + +import io.ktor.application.ApplicationCall +import io.ktor.auth.principal +import io.ktor.http.HttpStatusCode +import io.ktor.request.receiveStream +import io.ktor.response.respond +import io.mockk.Runs +import io.mockk.coEvery +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.mockkStatic +import kotlinx.coroutines.runBlocking +import org.briarproject.mailbox.core.TestUtils.getNewRandomId +import org.briarproject.mailbox.core.server.AuthManager +import org.briarproject.mailbox.core.server.MailboxPrincipal +import org.briarproject.mailbox.core.system.RandomIdManager +import org.junit.jupiter.api.Assertions.assertArrayEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir +import java.io.ByteArrayInputStream +import java.io.File +import kotlin.random.Random +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class FileManagerTest { + + private val authManager: AuthManager = mockk() + private val fileProvider: FileProvider = mockk() + private val randomIdManager = RandomIdManager() + + private val fileManager = FileManager(authManager, fileProvider, randomIdManager) + + private val call: ApplicationCall = mockk() + private val id = getNewRandomId() + private val bytes = Random.nextBytes(2048) + + init { + mockkStatic("io.ktor.auth.AuthenticationKt") + mockkStatic("io.ktor.request.ApplicationReceiveFunctionsKt") + mockkStatic("io.ktor.response.ApplicationResponseFunctionsKt") + } + + @Test + fun `post new file stores file correctly`(@TempDir tempDir: File) = runBlocking { + val tmpFile = File(tempDir, "tmp") + val finalFile = File(tempDir, "final") + + every { call.principal<MailboxPrincipal>() } returns MailboxPrincipal.OwnerPrincipal + every { authManager.assertCanPostToFolder(MailboxPrincipal.OwnerPrincipal, id) } just Runs + every { fileProvider.getTemporaryFile(any()) } returns tmpFile + coEvery { call.receiveStream() } returns ByteArrayInputStream(bytes) + every { fileProvider.getFile(id, any()) } returns finalFile + coEvery { call.respond(HttpStatusCode.OK) } just Runs + + fileManager.postFile(call, id) + + assertFalse(tmpFile.exists()) + assertTrue(finalFile.exists()) + assertArrayEquals(bytes, finalFile.readBytes()) + } + +}