diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/web/rest/LibraryController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/web/rest/LibraryController.kt index 965dc436d..f5fa3ef19 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/web/rest/LibraryController.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/web/rest/LibraryController.kt @@ -37,27 +37,33 @@ class LibraryController( ) { @GetMapping - fun getAll(@AuthenticationPrincipal principal: KomgaPrincipal): List = + fun getAll( + @AuthenticationPrincipal principal: KomgaPrincipal + ): List = if (principal.user.sharedAllLibraries) { libraryRepository.findAll(Sort.by("name")) } else { principal.user.sharedLibraries - }.map { it.toDto() } + }.map { it.toDto(includeRoot = principal.user.isAdmin()) } @GetMapping("{id}") - fun getOne(@AuthenticationPrincipal principal: KomgaPrincipal, @PathVariable id: Long): LibraryDto = + fun getOne( + @AuthenticationPrincipal principal: KomgaPrincipal, + @PathVariable id: Long + ): LibraryDto = libraryRepository.findByIdOrNull(id)?.let { if (!principal.user.canAccessLibrary(it)) throw ResponseStatusException(HttpStatus.UNAUTHORIZED) - it.toDto() + it.toDto(includeRoot = principal.user.isAdmin()) } ?: throw ResponseStatusException(HttpStatus.NOT_FOUND) @PostMapping @PreAuthorize("hasRole('ADMIN')") fun addOne( + @AuthenticationPrincipal principal: KomgaPrincipal, @Valid @RequestBody library: LibraryCreationDto ): LibraryDto = try { - libraryLifecycle.addLibrary(Library(library.name, library.root)).toDto() + libraryLifecycle.addLibrary(Library(library.name, library.root)).toDto(includeRoot = principal.user.isAdmin()) } catch (e: Exception) { when (e) { is FileNotFoundException, @@ -90,8 +96,8 @@ data class LibraryDto( val root: String ) -fun Library.toDto() = LibraryDto( +fun Library.toDto(includeRoot: Boolean) = LibraryDto( id = id, name = name, - root = root.toString() + root = if (includeRoot) root.toURI().path else "" ) diff --git a/komga/src/test/kotlin/org/gotson/komga/interfaces/web/rest/LibraryControllerTest.kt b/komga/src/test/kotlin/org/gotson/komga/interfaces/web/rest/LibraryControllerTest.kt index 874e7cf23..f5f419ade 100644 --- a/komga/src/test/kotlin/org/gotson/komga/interfaces/web/rest/LibraryControllerTest.kt +++ b/komga/src/test/kotlin/org/gotson/komga/interfaces/web/rest/LibraryControllerTest.kt @@ -1,6 +1,11 @@ package org.gotson.komga.interfaces.web.rest +import org.gotson.komga.domain.model.UserRoles +import org.gotson.komga.domain.model.makeLibrary +import org.gotson.komga.domain.persistence.LibraryRepository import org.gotson.komga.interfaces.web.WithMockCustomUser +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -19,10 +24,23 @@ import org.springframework.test.web.servlet.post @SpringBootTest @AutoConfigureMockMvc(printOnlyOnFailure = false) class LibraryControllerTest( - @Autowired private val mockMvc: MockMvc + @Autowired private val mockMvc: MockMvc, + @Autowired private val libraryRepository: LibraryRepository ) { private val route = "/api/v1/libraries" + private val library = makeLibrary(url = "file:/library1") + + @BeforeAll + fun `setup library`() { + libraryRepository.save(library) + } + + @AfterAll + fun `teardown library`() { + libraryRepository.deleteAll() + } + @Nested inner class AnonymousUser { @Test @@ -78,4 +96,39 @@ class LibraryControllerTest( } } } + + @Nested + inner class DtoRootSanitization { + @Test + @WithMockCustomUser + fun `given regular user when getting libraries then root is hidden`() { + mockMvc.get(route) + .andExpect { + status { isOk } + jsonPath("$[0].root") { value("") } + } + + mockMvc.get("${route}/${library.id}") + .andExpect { + status { isOk } + jsonPath("$.root") { value("") } + } + } + + @Test + @WithMockCustomUser(roles = [UserRoles.ADMIN]) + fun `given admin user when getting books then root is available`() { + mockMvc.get(route) + .andExpect { + status { isOk } + jsonPath("$[0].root") { value("/library1") } + } + + mockMvc.get("${route}/${library.id}") + .andExpect { + status { isOk } + jsonPath("$.root") { value("/library1") } + } + } + } }