Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequ
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
import org.springframework.test.web.servlet.result.MockMvcResultHandlers
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status

Expand Down Expand Up @@ -658,4 +659,40 @@ class WorkspaceControllerTests : ControllerTestBase() {
document(
"organizations/{organization_id}/workspaces/{workspace_id}/files/download/GET"))
}

@Test
@WithMockOauth2User
fun `download workspace file with wrong file name`() {

val workspaceId =
createWorkspaceAndReturnId(
mvc, organizationId, constructWorkspaceCreateRequest(solutionId = solutionId))

val fileName = "test.txt"
val fileToUpload =
this::class.java.getResourceAsStream("/workspace/$fileName")
?: throw IllegalStateException(
"$fileName file used for organizations/{organization_id}/workspaces/{workspace_id}/files/POST endpoint documentation cannot be null")

val mockFile =
MockMultipartFile(
"file", fileName, MediaType.TEXT_PLAIN_VALUE, IOUtils.toByteArray(fileToUpload))

val destination = "path/to/a/directory/"
mvc.perform(
multipart("/organizations/$organizationId/workspaces/$workspaceId/files")
.file(mockFile)
.param("overwrite", "true")
.param("destination", destination)
.accept(MediaType.APPLICATION_JSON)
.with(csrf()))

mvc.perform(
get("/organizations/$organizationId/workspaces/$workspaceId/files/download")
.param("file_name", "Wrong file name")
.accept(MediaType.APPLICATION_OCTET_STREAM))
.andExpect(status().is4xxClientError)
.andExpect(jsonPath("$.detail").value("Wrong file name does not exist."))
.andDo(MockMvcResultHandlers.print())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license.
package com.cosmotech.common.exceptions

import io.awspring.cloud.s3.S3Exception
import java.net.URI
import org.apache.commons.lang3.NotImplementedException
import org.springframework.core.Ordered
Expand All @@ -21,7 +22,9 @@ import org.springframework.web.bind.annotation.RestControllerAdvice
import org.springframework.web.context.request.WebRequest
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
import org.springframework.web.util.BindErrorUtils
import software.amazon.awssdk.services.s3.model.NoSuchKeyException

@Suppress("TooManyFunctions")
@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice
open class CsmExceptionHandling : ResponseEntityExceptionHandler() {
Expand Down Expand Up @@ -160,4 +163,24 @@ open class CsmExceptionHandling : ResponseEntityExceptionHandler() {
}
return response
}

@ExceptionHandler(NoSuchKeyException::class)
fun handleNoSuchKeyException(): ProblemDetail {
val response = ProblemDetail.forStatus(HttpStatus.NOT_FOUND)
val noSuchKeyErrorStatus = HttpStatus.NOT_FOUND
response.type = URI.create(httpStatusCodeTypePrefix + noSuchKeyErrorStatus.value())
response.detail = "The specified file name does not exist."
return response
}

@ExceptionHandler(S3Exception::class)
fun handleS3Exception(exception: S3Exception): ProblemDetail {
val response = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR)
val internalServerErrorStatus = HttpStatus.INTERNAL_SERVER_ERROR
response.type = URI.create(httpStatusCodeTypePrefix + internalServerErrorStatus.value())
if (exception.message != null) {
response.detail = exception.message
}
return response
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ import org.springframework.mock.web.MockMultipartFile
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.context.junit4.SpringRunner
import software.amazon.awssdk.services.s3.model.NoSuchKeyException

@ActiveProfiles(profiles = ["workspace-test"])
@ExtendWith(MockKExtension::class)
Expand Down Expand Up @@ -177,6 +176,24 @@ class WorkspaceServiceIntegrationTest : CsmTestBase() {
assertEquals(expectedText, retrievedText)
}

@Test
fun `test get workspace file with wrong name`() {
val resourceTestFile = resourceLoader.getResource("classpath:/$fileName").file
val input = FileInputStream(resourceTestFile)
val multipartFile =
MockMultipartFile(
"file", resourceTestFile.getName(), "text/plain", IOUtils.toByteArray(input))
workspaceApiService.createWorkspaceFile(
organizationSaved.id, workspaceSaved.id, multipartFile, true, null)

val exception =
assertThrows<CsmResourceNotFoundException> {
workspaceApiService.getWorkspaceFile(organizationSaved.id, workspaceSaved.id, "WrongName")
}

assertEquals("WrongName does not exist.", exception.message)
}

@Test
fun `test list workspace files`() {
every { getCurrentAuthenticatedRoles(any()) } returns listOf("Platform.Admin")
Expand Down Expand Up @@ -204,21 +221,21 @@ class WorkspaceServiceIntegrationTest : CsmTestBase() {
logger.info("should delete a workspace file")
val resourceTestFile = resourceLoader.getResource("classpath:/$fileName").file
val input = FileInputStream(resourceTestFile)
val originalFilename = resourceTestFile.getName()
val multipartFile =
MockMultipartFile(
"file", resourceTestFile.getName(), "text/plain", IOUtils.toByteArray(input))
MockMultipartFile("file", originalFilename, "text/plain", IOUtils.toByteArray(input))

workspaceApiService.createWorkspaceFile(
organizationSaved.id, workspaceSaved.id, multipartFile, true, null)

workspaceApiService.deleteWorkspaceFile(organizationSaved.id, workspaceSaved.id, fileName)

val exception =
assertThrows<NoSuchKeyException> {
assertThrows<CsmResourceNotFoundException> {
workspaceApiService.getWorkspaceFile(organizationSaved.id, workspaceSaved.id, fileName)
}

assertEquals("The specified key does not exist.", exception.awsErrorDetails().errorMessage())
assertEquals("$originalFilename does not exist.", exception.message)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import org.springframework.web.multipart.MultipartFile
import software.amazon.awssdk.awscore.exception.AwsServiceException
import software.amazon.awssdk.services.s3.S3Client
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request
import software.amazon.awssdk.services.s3.model.NoSuchKeyException

private const val WORKSPACE_FILES_BASE_FOLDER = "workspace-files"

Expand Down Expand Up @@ -233,12 +234,20 @@ internal class WorkspaceServiceImpl(
workspace.id,
workspace.name,
fileName)
return InputStreamResource(
s3Template
.download(
csmPlatformProperties.s3.bucketName,
"$organizationId/$workspaceId/$WORKSPACE_FILES_BASE_FOLDER/$fileName")
.inputStream)
var fileResource: Resource
try {
fileResource =
InputStreamResource(
s3Template
.download(
csmPlatformProperties.s3.bucketName,
"$organizationId/$workspaceId/$WORKSPACE_FILES_BASE_FOLDER/$fileName")
.inputStream)
} catch (exception: NoSuchKeyException) {
throw CsmResourceNotFoundException("$fileName does not exist.", exception)
}

return fileResource
}

override fun createWorkspaceFile(
Expand Down Expand Up @@ -326,13 +335,23 @@ internal class WorkspaceServiceImpl(
.prefix(prefix)
.build()

return s3Client
.listObjectsV2Paginator(listObjectsRequest)
.stream()
.flatMap {
it.contents().stream().map { WorkspaceFile(fileName = it.key().removePrefix(prefix)) }
}
.toList()
try {
return s3Client
.listObjectsV2Paginator(listObjectsRequest)
.stream()
.flatMap { s3Object ->
s3Object.contents().stream().map {
WorkspaceFile(fileName = it.key().removePrefix(prefix))
}
}
.toList()
} catch (e: AwsServiceException) {
throw S3Exception("Something wrong happened when listing workspace files", e)
} catch (e: SdkClientException) {
throw S3Exception("Something wrong happened when listing workspace files", e)
} catch (e: S3Exception) {
throw S3Exception("Something wrong happened when listing workspace files", e)
}
}

private fun deleteS3WorkspaceObject(
Expand Down
Loading