diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/AppModule.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/AppModule.kt index d7cee665e1be3fa7f82e511bd2a29ca4f6ab52d8..1e1469a6010e29cc602d9fa4c481cc3bd64c3040 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/AppModule.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/AppModule.kt @@ -32,10 +32,10 @@ internal class AppModule { @Provides fun provideFileProvider(app: Application) = object : FileProvider { private val tempFilesDir = File(app.applicationContext.cacheDir, "tmp").also { it.mkdirs() } - private val filesDir = app.applicationContext.getDir("folders", MODE_PRIVATE) + override val folderRoot = app.applicationContext.getDir("folders", MODE_PRIVATE) override fun getTemporaryFile(fileId: String) = File(tempFilesDir, fileId) - override fun getFolder(folderId: String) = File(filesDir, folderId).also { it.mkdirs() } + override fun getFolder(folderId: String) = File(folderRoot, folderId).also { it.mkdirs() } override fun getFile(folderId: String, fileId: String) = File(getFolder(folderId), fileId) } } diff --git a/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/JavaCliModule.kt b/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/JavaCliModule.kt index 18263ad275ae7bc527e2acba979c0b4a7330f9fe..e9d7cb747c13f828a8a8e7fe751d6129cf12276a 100644 --- a/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/JavaCliModule.kt +++ b/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/JavaCliModule.kt @@ -86,10 +86,10 @@ internal class JavaCliModule { @Provides fun provideFileProvider() = object : FileProvider { private val tempFilesDir = File(dataDir, "tmp").also { it.mkdirs() } - private val filesDir = File(dataDir, "folders").also { it.mkdirs() } + override val folderRoot = File(dataDir, "folders").also { it.mkdirs() } override fun getTemporaryFile(fileId: String) = File(tempFilesDir, fileId) - override fun getFolder(folderId: String) = File(filesDir, folderId).also { it.mkdirs() } + override fun getFolder(folderId: String) = File(folderRoot, folderId).also { it.mkdirs() } override fun getFile(folderId: String, fileId: String) = File(getFolder(folderId), fileId) } 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 6e7aac6556a17eb9c9ff2fa0590a990be08d230a..f38e3af89595ce62d02de84c6ae9018a65a33755 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 @@ -14,8 +14,11 @@ import org.briarproject.mailbox.core.server.AuthManager import org.briarproject.mailbox.core.server.MailboxPrincipal import org.briarproject.mailbox.core.system.InvalidIdException import org.briarproject.mailbox.core.system.RandomIdManager +import org.slf4j.LoggerFactory.getLogger import javax.inject.Inject +private val LOG = getLogger(FileManager::class.java) + class FileManager @Inject constructor( private val db: Database, private val authManager: AuthManager, @@ -134,6 +137,20 @@ class FileManager @Inject constructor( } call.respond(folderListResponse) } + + fun deleteAllFiles(): Boolean { + var allDeleted = true + fileProvider.folderRoot.listFiles()?.forEach { folder -> + if (!folder.deleteRecursively()) { + allDeleted = false + LOG.warn("Not everything in $folder could get deleted.") + } + } ?: run { + allDeleted = false + LOG.warn("Could not delete folders.") + } + return allDeleted + } } data class FileListResponse(val files: List<FileResponse>) diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/files/FileProvider.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/files/FileProvider.kt index 7c01845b84c65431e2073235ca54ab339680e810..58ff7c0d9958c593e15bbf118bcb9499fc286b2d 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/files/FileProvider.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/files/FileProvider.kt @@ -3,6 +3,7 @@ package org.briarproject.mailbox.core.files import java.io.File interface FileProvider { + val folderRoot: File fun getTemporaryFile(fileId: String): File fun getFolder(folderId: String): File fun getFile(folderId: String, fileId: String): File diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestComponent.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestComponent.kt index a21277876b3f7b760a9ec9bac1397a11bd6fad11..c374303c762f7845193081bf282b218a0dca1830 100644 --- a/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestComponent.kt +++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestComponent.kt @@ -2,6 +2,7 @@ package org.briarproject.mailbox.core import dagger.Component import org.briarproject.mailbox.core.db.Database +import org.briarproject.mailbox.core.files.FileManager import org.briarproject.mailbox.core.lifecycle.LifecycleManager import org.briarproject.mailbox.core.settings.SettingsManager import org.briarproject.mailbox.core.system.RandomIdManager @@ -17,6 +18,7 @@ interface TestComponent { fun injectCoreEagerSingletons(): CoreEagerSingletons fun getLifecycleManager(): LifecycleManager fun getSettingsManager(): SettingsManager + fun getFileManager(): FileManager fun getDatabase(): Database fun getRandomIdManager(): RandomIdManager } diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestModule.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestModule.kt index 571adf9d7b159c8dce26956d381fa13aa62d2030..9b270b45e2c69a0a5783fc7659c1fff0d1b7b140 100644 --- a/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestModule.kt +++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestModule.kt @@ -41,10 +41,10 @@ internal class TestModule(private val tempDir: File) { @Provides fun provideFileProvider() = object : FileProvider { private val tempFilesDir = File(tempDir, "tmp").also { it.mkdirs() } - private val filesDir = File(tempDir, "folders") + override val folderRoot = File(tempDir, "folders") override fun getTemporaryFile(fileId: String) = File(tempFilesDir, fileId) - override fun getFolder(folderId: String) = File(filesDir, folderId).also { it.mkdirs() } + override fun getFolder(folderId: String) = File(folderRoot, folderId).also { it.mkdirs() } override fun getFile(folderId: String, fileId: String) = File(getFolder(folderId), fileId) } } 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 index ad2363bf49fb1bf85b111dc47069885a5583ea6f..ef5cd61683f80bd8c152908704094441c6af3ff0 100644 --- 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 @@ -11,14 +11,12 @@ 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.AfterEach import org.junit.jupiter.api.Assertions.assertArrayEquals 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) @@ -29,13 +27,18 @@ class FileManagerIntegrationTest : IntegrationTest() { addContact(contact2) } + @AfterEach + fun cleanUp() { + testComponent.getFileManager().deleteAllFiles() + } + @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) + assertEquals(HttpStatusCode.Unauthorized, response.status) } @Test @@ -44,7 +47,7 @@ class FileManagerIntegrationTest : IntegrationTest() { authenticateWithToken(ownerToken) body = bytes } - assertEquals(HttpStatusCode.Unauthorized.value, response.status.value) + assertEquals(HttpStatusCode.Unauthorized, response.status) } @Test @@ -53,7 +56,7 @@ class FileManagerIntegrationTest : IntegrationTest() { authenticateWithToken(ownerToken) body = bytes } - assertEquals(HttpStatusCode.BadRequest.value, response.status.value) + assertEquals(HttpStatusCode.BadRequest, response.status) assertEquals("Malformed ID: foo", response.readText()) } @@ -64,13 +67,13 @@ class FileManagerIntegrationTest : IntegrationTest() { authenticateWithToken(ownerToken) body = bytes } - assertEquals(HttpStatusCode.OK.value, response.status.value) + assertEquals(HttpStatusCode.OK, response.status) // contact can list the file val listResponse: HttpResponse = httpClient.get("$baseUrl/files/${contact1.inboxId}") { authenticateWithToken(contact1.token) } - assertEquals(HttpStatusCode.OK.value, listResponse.status.value) + assertEquals(HttpStatusCode.OK, listResponse.status) val fileList: FileListResponse = listResponse.receive() assertEquals(1, fileList.files.size) @@ -80,7 +83,7 @@ class FileManagerIntegrationTest : IntegrationTest() { httpClient.get("$baseUrl/files/${contact1.inboxId}/$fileId") { authenticateWithToken(contact1.token) } - assertEquals(HttpStatusCode.OK.value, fileResponse.status.value) + assertEquals(HttpStatusCode.OK, fileResponse.status) assertArrayEquals(bytes, fileResponse.readBytes()) // contact can delete the file @@ -88,7 +91,7 @@ class FileManagerIntegrationTest : IntegrationTest() { httpClient.delete("$baseUrl/files/${contact1.inboxId}/$fileId") { authenticateWithToken(contact1.token) } - assertEquals(HttpStatusCode.OK.value, deleteResponse.status.value) + assertEquals(HttpStatusCode.OK, deleteResponse.status) // now the list of files in that folder is empty again val emptyFileListResponse: FileListResponse = @@ -103,15 +106,33 @@ class FileManagerIntegrationTest : IntegrationTest() { val response: HttpResponse = httpClient.get("$baseUrl/files/${getNewRandomId()}") { authenticateWithToken(token) } - assertEquals(HttpStatusCode.Unauthorized.value, response.status.value) + assertEquals(HttpStatusCode.Unauthorized, response.status) + + // upload a real file + val postResponse: HttpResponse = httpClient.post("$baseUrl/files/${contact1.inboxId}") { + authenticateWithToken(ownerToken) + body = bytes + } + assertEquals(HttpStatusCode.OK, postResponse.status) + + // wrong token also gets rejected for real folder + val lastResponse: HttpResponse = httpClient.get("$baseUrl/files/${contact1.inboxId}") { + authenticateWithToken(token) + } + assertEquals(HttpStatusCode.Unauthorized, lastResponse.status) } @Test fun `list files rejects unauthorized folder ID`(): Unit = runBlocking { - val response: HttpResponse = httpClient.get("$baseUrl/files/${contact1.inboxId}") { + val response1: HttpResponse = httpClient.get("$baseUrl/files/${contact1.inboxId}") { authenticateWithToken(ownerToken) } - assertEquals(HttpStatusCode.Unauthorized.value, response.status.value) + assertEquals(HttpStatusCode.Unauthorized, response1.status) + + val response2: HttpResponse = httpClient.get("$baseUrl/files/${contact1.inboxId}") { + authenticateWithToken(contact2.token) + } + assertEquals(HttpStatusCode.Unauthorized, response2.status) } @Test @@ -119,7 +140,7 @@ class FileManagerIntegrationTest : IntegrationTest() { val response: HttpResponse = httpClient.get("$baseUrl/files/foo") { authenticateWithToken(ownerToken) } - assertEquals(HttpStatusCode.BadRequest.value, response.status.value) + assertEquals(HttpStatusCode.BadRequest, response.status) assertEquals("Malformed ID: foo", response.readText()) } @@ -128,7 +149,7 @@ class FileManagerIntegrationTest : IntegrationTest() { val response: HttpResponse = httpClient.get("$baseUrl/files/${contact1.outboxId}") { authenticateWithToken(ownerToken) } - assertEquals(HttpStatusCode.OK.value, response.status.value) + assertEquals(HttpStatusCode.OK, response.status) assertEquals("""{"files":[]}""", response.readText()) } @@ -138,7 +159,7 @@ class FileManagerIntegrationTest : IntegrationTest() { httpClient.get("$baseUrl/files/${getNewRandomId()}/${getNewRandomId()}") { authenticateWithToken(token) } - assertEquals(HttpStatusCode.Unauthorized.value, response.status.value) + assertEquals(HttpStatusCode.Unauthorized, response.status) } @Test @@ -147,7 +168,7 @@ class FileManagerIntegrationTest : IntegrationTest() { httpClient.get("$baseUrl/files/${contact1.inboxId}/${getNewRandomId()}") { authenticateWithToken(ownerToken) } - assertEquals(HttpStatusCode.Unauthorized.value, response.status.value) + assertEquals(HttpStatusCode.Unauthorized, response.status) } @Test @@ -155,7 +176,7 @@ class FileManagerIntegrationTest : IntegrationTest() { val response: HttpResponse = httpClient.get("$baseUrl/files/foo/${getNewRandomId()}") { authenticateWithToken(ownerToken) } - assertEquals(HttpStatusCode.BadRequest.value, response.status.value) + assertEquals(HttpStatusCode.BadRequest, response.status) assertEquals("Malformed ID: foo", response.readText()) } @@ -164,7 +185,7 @@ class FileManagerIntegrationTest : IntegrationTest() { val response: HttpResponse = httpClient.get("$baseUrl/files/${contact1.outboxId}/bar") { authenticateWithToken(ownerToken) } - assertEquals(HttpStatusCode.BadRequest.value, response.status.value) + assertEquals(HttpStatusCode.BadRequest, response.status) assertEquals("Malformed ID: bar", response.readText()) } @@ -174,7 +195,7 @@ class FileManagerIntegrationTest : IntegrationTest() { val response: HttpResponse = httpClient.get("$baseUrl/files/${contact1.outboxId}/$id") { authenticateWithToken(ownerToken) } - assertEquals(HttpStatusCode.NotFound.value, response.status.value) + assertEquals(HttpStatusCode.NotFound, response.status) } @Test @@ -183,7 +204,7 @@ class FileManagerIntegrationTest : IntegrationTest() { httpClient.delete("$baseUrl/files/${getNewRandomId()}/${getNewRandomId()}") { authenticateWithToken(token) } - assertEquals(HttpStatusCode.Unauthorized.value, response.status.value) + assertEquals(HttpStatusCode.Unauthorized, response.status) } @Test @@ -192,7 +213,7 @@ class FileManagerIntegrationTest : IntegrationTest() { httpClient.delete("$baseUrl/files/${contact1.inboxId}/${getNewRandomId()}") { authenticateWithToken(ownerToken) } - assertEquals(HttpStatusCode.Unauthorized.value, response.status.value) + assertEquals(HttpStatusCode.Unauthorized, response.status) } @Test @@ -200,7 +221,7 @@ class FileManagerIntegrationTest : IntegrationTest() { val response: HttpResponse = httpClient.delete("$baseUrl/files/foo/${getNewRandomId()}") { authenticateWithToken(ownerToken) } - assertEquals(HttpStatusCode.BadRequest.value, response.status.value) + assertEquals(HttpStatusCode.BadRequest, response.status) assertEquals("Malformed ID: foo", response.readText()) } @@ -209,7 +230,7 @@ class FileManagerIntegrationTest : IntegrationTest() { val response: HttpResponse = httpClient.delete("$baseUrl/files/${contact1.outboxId}/bar") { authenticateWithToken(ownerToken) } - assertEquals(HttpStatusCode.BadRequest.value, response.status.value) + assertEquals(HttpStatusCode.BadRequest, response.status) assertEquals("Malformed ID: bar", response.readText()) } @@ -219,7 +240,7 @@ class FileManagerIntegrationTest : IntegrationTest() { val response: HttpResponse = httpClient.delete("$baseUrl/files/${contact1.outboxId}/$id") { authenticateWithToken(ownerToken) } - assertEquals(HttpStatusCode.NotFound.value, response.status.value) + assertEquals(HttpStatusCode.NotFound, response.status) } @Test @@ -227,7 +248,7 @@ class FileManagerIntegrationTest : IntegrationTest() { val response: HttpResponse = httpClient.get("$baseUrl/folders") { authenticateWithToken(contact1.token) } - assertEquals(HttpStatusCode.Unauthorized.value, response.status.value) + assertEquals(HttpStatusCode.Unauthorized, response.status) } @Test @@ -235,7 +256,7 @@ class FileManagerIntegrationTest : IntegrationTest() { val response: HttpResponse = httpClient.get("$baseUrl/folders") { authenticateWithToken(ownerToken) } - assertEquals(HttpStatusCode.OK.value, response.status.value) + assertEquals(HttpStatusCode.OK, response.status) assertEquals("""{"folders":[]}""", response.readText()) } @@ -246,43 +267,20 @@ class FileManagerIntegrationTest : IntegrationTest() { authenticateWithToken(contact1.token) body = bytes } - assertEquals(HttpStatusCode.OK.value, response1.status.value) + assertEquals(HttpStatusCode.OK, response1.status) // contact2 uploads a file val response2: HttpResponse = httpClient.post("$baseUrl/files/${contact2.outboxId}") { authenticateWithToken(contact2.token) body = bytes } - assertEquals(HttpStatusCode.OK.value, response2.status.value) + assertEquals(HttpStatusCode.OK, response2.status) // owner now sees both contacts' outboxes in folders list val folderListResponse: FolderListResponse = httpClient.get("$baseUrl/folders") { authenticateWithToken(ownerToken) } - val folderList = - listOf(FolderResponse(contact1.outboxId), FolderResponse(contact2.outboxId)) - assertEquals(FolderListResponse(folderList), folderListResponse) - - // get the file IDs to be able to delete them to not interfere with other tests - val fileListResponse1: FileListResponse = - httpClient.get("$baseUrl/files/${contact1.outboxId}") { - authenticateWithToken(ownerToken) - } - val id1 = fileListResponse1.files[0].name - val deleteResponse1: HttpResponse = - httpClient.delete("$baseUrl/files/${contact1.outboxId}/$id1") { - authenticateWithToken(ownerToken) - } - assertEquals(HttpStatusCode.OK.value, deleteResponse1.status.value) - val fileListResponse2: FileListResponse = - httpClient.get("$baseUrl/files/${contact2.outboxId}") { - authenticateWithToken(ownerToken) - } - val id2 = fileListResponse2.files[0].name - val deleteResponse2: HttpResponse = - httpClient.delete("$baseUrl/files/${contact2.outboxId}/$id2") { - authenticateWithToken(ownerToken) - } - assertEquals(HttpStatusCode.OK.value, deleteResponse2.status.value) + val folderList = setOf(FolderResponse(contact1.outboxId), FolderResponse(contact2.outboxId)) + assertEquals(folderList, folderListResponse.folders.toSet()) } } 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 index 638ed6afe226aae99cf46b2b62ed18accc2ccfd0..49cba822da140b9eaead72788b1b1986d2acf890 100644 --- 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 @@ -16,6 +16,7 @@ import org.briarproject.mailbox.core.TestUtils.getNewRandomId import org.briarproject.mailbox.core.db.Database import org.briarproject.mailbox.core.server.AuthManager import org.briarproject.mailbox.core.server.MailboxPrincipal +import org.briarproject.mailbox.core.server.MailboxPrincipal.OwnerPrincipal import org.briarproject.mailbox.core.system.RandomIdManager import org.junit.jupiter.api.Assertions.assertArrayEquals import org.junit.jupiter.api.Test @@ -50,8 +51,8 @@ class FileManagerTest { 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 { call.principal<MailboxPrincipal>() } returns OwnerPrincipal + every { authManager.assertCanPostToFolder(OwnerPrincipal, id) } just Runs every { fileProvider.getTemporaryFile(any()) } returns tmpFile coEvery { call.receiveStream() } returns ByteArrayInputStream(bytes) every { fileProvider.getFile(id, any()) } returns finalFile