diff --git a/komga/src/flyway/resources/db/migration/V20200609173933__user_roles.sql b/komga/src/flyway/resources/db/migration/V20200609173933__user_roles.sql new file mode 100644 index 000000000..a9747ea6f --- /dev/null +++ b/komga/src/flyway/resources/db/migration/V20200609173933__user_roles.sql @@ -0,0 +1,4 @@ +alter table user + add column role_file_download boolean default true; +alter table user + add column role_page_streaming boolean default true; diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/model/KomgaUser.kt b/komga/src/main/kotlin/org/gotson/komga/domain/model/KomgaUser.kt index 87ed762b5..0c8fde46d 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/model/KomgaUser.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/model/KomgaUser.kt @@ -4,6 +4,11 @@ import java.time.LocalDateTime import javax.validation.constraints.Email import javax.validation.constraints.NotBlank +const val ROLE_USER = "USER" +const val ROLE_ADMIN = "ADMIN" +const val ROLE_FILE_DOWNLOAD = "FILE_DOWNLOAD" +const val ROLE_PAGE_STREAMING = "PAGE_STREAMING" + data class KomgaUser( @Email @NotBlank @@ -11,6 +16,8 @@ data class KomgaUser( @NotBlank val password: String, val roleAdmin: Boolean, + val roleFileDownload: Boolean = true, + val rolePageStreaming: Boolean = true, val sharedLibrariesIds: Set = emptySet(), val sharedAllLibraries: Boolean = true, val id: Long = 0, @@ -19,8 +26,10 @@ data class KomgaUser( ) : Auditable() { fun roles(): Set { - val roles = mutableSetOf("USER") - if (roleAdmin) roles.add("ADMIN") + val roles = mutableSetOf(ROLE_USER) + if (roleAdmin) roles.add(ROLE_ADMIN) + if (roleFileDownload) roles.add(ROLE_FILE_DOWNLOAD) + if (rolePageStreaming) roles.add(ROLE_PAGE_STREAMING) return roles } @@ -52,4 +61,8 @@ data class KomgaUser( fun canAccessLibrary(library: Library): Boolean { return sharedAllLibraries || sharedLibrariesIds.any { it == library.id } } + + override fun toString(): String { + return "KomgaUser(email='$email', roleAdmin=$roleAdmin, roleFileDownload=$roleFileDownload, rolePageStreaming=$rolePageStreaming, sharedLibrariesIds=$sharedLibrariesIds, sharedAllLibraries=$sharedAllLibraries, id=$id, createdDate=$createdDate, lastModifiedDate=$lastModifiedDate)" + } } diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaUserDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaUserDao.kt index 4e6fc4ee0..7a8a6998e 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaUserDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaUserDao.kt @@ -44,6 +44,8 @@ class KomgaUserDao( email = ur.email, password = ur.password, roleAdmin = ur.roleAdmin, + roleFileDownload = ur.roleFileDownload, + rolePageStreaming = ur.rolePageStreaming, sharedLibrariesIds = ulr.mapNotNull { it.libraryId }.toSet(), sharedAllLibraries = ur.sharedAllLibraries, id = ur.id, @@ -65,10 +67,12 @@ class KomgaUserDao( .set(u.EMAIL, user.email) .set(u.PASSWORD, user.password) .set(u.ROLE_ADMIN, user.roleAdmin) + .set(u.ROLE_FILE_DOWNLOAD, user.roleFileDownload) + .set(u.ROLE_PAGE_STREAMING, user.rolePageStreaming) .set(u.SHARED_ALL_LIBRARIES, user.sharedAllLibraries) .set(u.LAST_MODIFIED_DATE, LocalDateTime.now()) - .whenNotMatchedThenInsert(u.ID, u.EMAIL, u.PASSWORD, u.ROLE_ADMIN, u.SHARED_ALL_LIBRARIES) - .values(id, user.email, user.password, user.roleAdmin, user.sharedAllLibraries) + .whenNotMatchedThenInsert(u.ID, u.EMAIL, u.PASSWORD, u.ROLE_ADMIN, u.ROLE_FILE_DOWNLOAD, u.ROLE_PAGE_STREAMING, u.SHARED_ALL_LIBRARIES) + .values(id, user.email, user.password, user.roleAdmin, user.roleFileDownload, user.rolePageStreaming, user.sharedAllLibraries) .execute() deleteFrom(ul) diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/SecurityConfiguration.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/SecurityConfiguration.kt index 79580ee8f..b6a75d1e9 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/SecurityConfiguration.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/SecurityConfiguration.kt @@ -1,6 +1,8 @@ package org.gotson.komga.infrastructure.security import mu.KotlinLogging +import org.gotson.komga.domain.model.ROLE_ADMIN +import org.gotson.komga.domain.model.ROLE_USER import org.gotson.komga.infrastructure.configuration.KomgaProperties import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest import org.springframework.boot.autoconfigure.security.servlet.PathRequest @@ -37,16 +39,16 @@ class SecurityConfiguration( .authorizeRequests() // restrict all actuator endpoints to ADMIN only - .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ADMIN") + .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole(ROLE_ADMIN) // restrict H2 console to ADMIN only - .requestMatchers(PathRequest.toH2Console()).hasRole("ADMIN") + .requestMatchers(PathRequest.toH2Console()).hasRole(ROLE_ADMIN) // all other endpoints are restricted to authenticated users .antMatchers( "/api/**", "/opds/**" - ).hasRole("USER") + ).hasRole(ROLE_USER) // authorize frames for H2 console .and() diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/BookController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/BookController.kt index 0cca3013c..d56c6812f 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/BookController.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/BookController.kt @@ -12,6 +12,9 @@ import org.gotson.komga.domain.model.BookSearchWithReadProgress import org.gotson.komga.domain.model.ImageConversionException import org.gotson.komga.domain.model.Media import org.gotson.komga.domain.model.MediaNotReadyException +import org.gotson.komga.domain.model.ROLE_ADMIN +import org.gotson.komga.domain.model.ROLE_FILE_DOWNLOAD +import org.gotson.komga.domain.model.ROLE_PAGE_STREAMING import org.gotson.komga.domain.model.ReadStatus import org.gotson.komga.domain.persistence.BookMetadataRepository import org.gotson.komga.domain.persistence.BookRepository @@ -206,6 +209,7 @@ class BookController( "api/v1/books/{bookId}/file/*", "opds/v1.2/books/{bookId}/file/*" ], produces = [MediaType.APPLICATION_OCTET_STREAM_VALUE]) + @PreAuthorize("hasRole('$ROLE_FILE_DOWNLOAD')") fun getBookFile( @AuthenticationPrincipal principal: KomgaPrincipal, @PathVariable bookId: Long @@ -258,6 +262,7 @@ class BookController( "api/v1/books/{bookId}/pages/{pageNumber}", "opds/v1.2/books/{bookId}/pages/{pageNumber}" ]) + @PreAuthorize("hasRole('$ROLE_PAGE_STREAMING')") fun getBookPage( @AuthenticationPrincipal principal: KomgaPrincipal, request: WebRequest, @@ -345,7 +350,7 @@ class BookController( } ?: throw ResponseStatusException(HttpStatus.NOT_FOUND) @PostMapping("api/v1/books/{bookId}/analyze") - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") @ResponseStatus(HttpStatus.ACCEPTED) fun analyze(@PathVariable bookId: Long) { bookRepository.findByIdOrNull(bookId)?.let { book -> @@ -354,7 +359,7 @@ class BookController( } @PostMapping("api/v1/books/{bookId}/metadata/refresh") - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") @ResponseStatus(HttpStatus.ACCEPTED) fun refreshMetadata(@PathVariable bookId: Long) { bookRepository.findByIdOrNull(bookId)?.let { book -> @@ -363,7 +368,7 @@ class BookController( } @PatchMapping("api/v1/books/{bookId}/metadata") - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") fun updateMetadata( @PathVariable bookId: Long, @Parameter(description = "Metadata fields to update. Set a field to null to unset the metadata. You can omit fields you don't want to update.") diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/LibraryController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/LibraryController.kt index 069048642..15f99bc0c 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/LibraryController.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/LibraryController.kt @@ -6,6 +6,7 @@ import org.gotson.komga.domain.model.DirectoryNotFoundException import org.gotson.komga.domain.model.DuplicateNameException import org.gotson.komga.domain.model.Library import org.gotson.komga.domain.model.PathContainedInPath +import org.gotson.komga.domain.model.ROLE_ADMIN import org.gotson.komga.domain.persistence.BookRepository import org.gotson.komga.domain.persistence.LibraryRepository import org.gotson.komga.domain.service.LibraryLifecycle @@ -59,7 +60,7 @@ class LibraryController( } ?: throw ResponseStatusException(HttpStatus.NOT_FOUND) @PostMapping - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") fun addOne( @AuthenticationPrincipal principal: KomgaPrincipal, @Valid @RequestBody library: LibraryCreationDto @@ -78,7 +79,7 @@ class LibraryController( } @DeleteMapping("/{id}") - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") @ResponseStatus(HttpStatus.NO_CONTENT) fun deleteOne(@PathVariable id: Long) { libraryRepository.findByIdOrNull(id)?.let { @@ -87,7 +88,7 @@ class LibraryController( } @PostMapping("{libraryId}/scan") - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") @ResponseStatus(HttpStatus.ACCEPTED) fun scan(@PathVariable libraryId: Long) { libraryRepository.findByIdOrNull(libraryId)?.let { library -> @@ -96,7 +97,7 @@ class LibraryController( } @PostMapping("{libraryId}/analyze") - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") @ResponseStatus(HttpStatus.ACCEPTED) fun analyze(@PathVariable libraryId: Long) { bookRepository.findAllIdByLibraryId(libraryId).forEach { @@ -105,7 +106,7 @@ class LibraryController( } @PostMapping("{libraryId}/metadata/refresh") - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") @ResponseStatus(HttpStatus.ACCEPTED) fun refreshMetadata(@PathVariable libraryId: Long) { bookRepository.findAllIdByLibraryId(libraryId).forEach { diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/SeriesController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/SeriesController.kt index bd1b96aa4..733a27173 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/SeriesController.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/SeriesController.kt @@ -9,6 +9,7 @@ import mu.KotlinLogging import org.gotson.komga.application.tasks.TaskReceiver import org.gotson.komga.domain.model.BookSearchWithReadProgress import org.gotson.komga.domain.model.Media +import org.gotson.komga.domain.model.ROLE_ADMIN import org.gotson.komga.domain.model.ReadStatus import org.gotson.komga.domain.model.SeriesMetadata import org.gotson.komga.domain.model.SeriesSearchWithReadProgress @@ -213,7 +214,7 @@ class SeriesController( } @PostMapping("{seriesId}/analyze") - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") @ResponseStatus(HttpStatus.ACCEPTED) fun analyze(@PathVariable seriesId: Long) { bookRepository.findAllIdBySeriesId(seriesId).forEach { @@ -222,7 +223,7 @@ class SeriesController( } @PostMapping("{seriesId}/metadata/refresh") - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") @ResponseStatus(HttpStatus.ACCEPTED) fun refreshMetadata(@PathVariable seriesId: Long) { bookRepository.findAllIdBySeriesId(seriesId).forEach { @@ -231,7 +232,7 @@ class SeriesController( } @PatchMapping("{seriesId}/metadata") - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") fun updateMetadata( @PathVariable seriesId: Long, @Parameter(description = "Metadata fields to update. Set a field to null to unset the metadata. You can omit fields you don't want to update.") diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/UserController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/UserController.kt index fc6b3f709..82f32d679 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/UserController.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/UserController.kt @@ -1,12 +1,16 @@ package org.gotson.komga.interfaces.rest import mu.KotlinLogging +import org.gotson.komga.domain.model.ROLE_ADMIN +import org.gotson.komga.domain.model.ROLE_FILE_DOWNLOAD +import org.gotson.komga.domain.model.ROLE_PAGE_STREAMING import org.gotson.komga.domain.model.UserEmailAlreadyExistsException import org.gotson.komga.domain.persistence.KomgaUserRepository import org.gotson.komga.domain.persistence.LibraryRepository import org.gotson.komga.domain.service.KomgaUserLifecycle import org.gotson.komga.infrastructure.security.KomgaPrincipal import org.gotson.komga.interfaces.rest.dto.PasswordUpdateDto +import org.gotson.komga.interfaces.rest.dto.RolesUpdateDto import org.gotson.komga.interfaces.rest.dto.SharedLibrariesUpdateDto import org.gotson.komga.interfaces.rest.dto.UserCreationDto import org.gotson.komga.interfaces.rest.dto.UserDto @@ -58,13 +62,13 @@ class UserController( } @GetMapping - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") fun getAll(): List = userRepository.findAll().map { it.toWithSharedLibrariesDto() } @PostMapping @ResponseStatus(HttpStatus.CREATED) - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") fun addOne(@Valid @RequestBody newUser: UserCreationDto): UserDto = try { userLifecycle.createUser(newUser.toDomain()).toDto() @@ -74,7 +78,7 @@ class UserController( @DeleteMapping("{id}") @ResponseStatus(HttpStatus.NO_CONTENT) - @PreAuthorize("hasRole('ADMIN') and #principal.user.id != #id") + @PreAuthorize("hasRole('$ROLE_ADMIN') and #principal.user.id != #id") fun delete( @PathVariable id: Long, @AuthenticationPrincipal principal: KomgaPrincipal @@ -84,9 +88,29 @@ class UserController( } ?: throw ResponseStatusException(HttpStatus.NOT_FOUND) } + @PatchMapping("{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + @PreAuthorize("hasRole('$ROLE_ADMIN') and #principal.user.id != #id") + fun updateUserRoles( + @PathVariable id: Long, + @Valid @RequestBody patch: RolesUpdateDto, + @AuthenticationPrincipal principal: KomgaPrincipal + ) { + userRepository.findByIdOrNull(id)?.let { user -> + val updatedUser = user.copy( + roleAdmin = patch.roles.contains(ROLE_ADMIN), + roleFileDownload = patch.roles.contains(ROLE_FILE_DOWNLOAD), + rolePageStreaming = patch.roles.contains(ROLE_PAGE_STREAMING) + ) + userRepository.save(updatedUser).also { + logger.info { "Updated user roles: $it" } + } + } ?: throw ResponseStatusException(HttpStatus.NOT_FOUND) + } + @PatchMapping("{id}/shared-libraries") @ResponseStatus(HttpStatus.NO_CONTENT) - @PreAuthorize("hasRole('ADMIN')") + @PreAuthorize("hasRole('$ROLE_ADMIN')") fun updateSharesLibraries( @PathVariable id: Long, @Valid @RequestBody sharedLibrariesUpdateDto: SharedLibrariesUpdateDto @@ -99,7 +123,9 @@ class UserController( .map { it.id } .toSet() ) - userRepository.save(updatedUser) + userRepository.save(updatedUser).also { + logger.info { "Updated user shared libraries: $it" } + } } ?: throw ResponseStatusException(HttpStatus.NOT_FOUND) } } diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/dto/UserDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/dto/UserDto.kt index ec874fcac..81c42e453 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/dto/UserDto.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/dto/UserDto.kt @@ -1,6 +1,9 @@ package org.gotson.komga.interfaces.rest.dto import org.gotson.komga.domain.model.KomgaUser +import org.gotson.komga.domain.model.ROLE_ADMIN +import org.gotson.komga.domain.model.ROLE_FILE_DOWNLOAD +import org.gotson.komga.domain.model.ROLE_PAGE_STREAMING import org.gotson.komga.infrastructure.security.KomgaPrincipal import javax.validation.constraints.Email import javax.validation.constraints.NotBlank @@ -47,7 +50,13 @@ data class UserCreationDto( val roles: List = emptyList() ) { fun toDomain(): KomgaUser = - KomgaUser(email, password, roleAdmin = roles.contains("ADMIN")) + KomgaUser( + email, + password, + roleAdmin = roles.contains(ROLE_ADMIN), + roleFileDownload = roles.contains(ROLE_FILE_DOWNLOAD), + rolePageStreaming = roles.contains(ROLE_PAGE_STREAMING) + ) } data class PasswordUpdateDto( @@ -58,3 +67,7 @@ data class SharedLibrariesUpdateDto( val all: Boolean, val libraryIds: Set ) + +data class RolesUpdateDto( + val roles: List +) diff --git a/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/BookControllerTest.kt b/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/BookControllerTest.kt index 053d69007..4fb6777c6 100644 --- a/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/BookControllerTest.kt +++ b/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/BookControllerTest.kt @@ -8,6 +8,7 @@ import org.gotson.komga.domain.model.BookPage import org.gotson.komga.domain.model.BookSearch import org.gotson.komga.domain.model.KomgaUser import org.gotson.komga.domain.model.Media +import org.gotson.komga.domain.model.ROLE_ADMIN import org.gotson.komga.domain.model.makeBook import org.gotson.komga.domain.model.makeLibrary import org.gotson.komga.domain.model.makeSeries @@ -218,6 +219,41 @@ class BookControllerTest( } } + @Nested + inner class RestrictedUserByRole { + @Test + @WithMockCustomUser(roles = []) + fun `given user without page streaming role when getting specific book page then returns unauthorized`() { + makeSeries(name = "series", libraryId = library.id).let { series -> + seriesLifecycle.createSeries(series).let { created -> + val books = listOf(makeBook("1", libraryId = library.id)) + seriesLifecycle.addBooks(created, books) + } + } + + val book = bookRepository.findAll().first() + + mockMvc.get("/api/v1/books/${book.id}/pages/1") + .andExpect { status { isForbidden } } + } + + @Test + @WithMockCustomUser(roles = []) + fun `given user without file download role when getting specific book file then returns unauthorized`() { + makeSeries(name = "series", libraryId = library.id).let { series -> + seriesLifecycle.createSeries(series).let { created -> + val books = listOf(makeBook("1", libraryId = library.id)) + seriesLifecycle.addBooks(created, books) + } + } + + val book = bookRepository.findAll().first() + + mockMvc.get("/api/v1/books/${book.id}/file") + .andExpect { status { isForbidden } } + } + } + @Nested inner class MediaNotReady { @Test @@ -404,7 +440,7 @@ class BookControllerTest( } @Test - @WithMockCustomUser(roles = ["ADMIN"]) + @WithMockCustomUser(roles = [ROLE_ADMIN]) fun `given admin user when getting books then full url is available`() { val createdSeries = makeSeries(name = "series", libraryId = library.id).let { series -> seriesLifecycle.createSeries(series).also { created -> @@ -550,7 +586,7 @@ class BookControllerTest( """{"authors":"[{"name":""}]"}""", """{"ageRating":-1}""" ]) - @WithMockCustomUser(roles = ["ADMIN"]) + @WithMockCustomUser(roles = [ROLE_ADMIN]) fun `given invalid json when updating metadata then raise validation error`(jsonString: String) { mockMvc.patch("/api/v1/books/1/metadata") { contentType = MediaType.APPLICATION_JSON @@ -561,7 +597,7 @@ class BookControllerTest( } @Test - @WithMockCustomUser(roles = ["ADMIN"]) + @WithMockCustomUser(roles = [ROLE_ADMIN]) fun `given valid json when updating metadata then fields are updated`() { makeSeries(name = "series", libraryId = library.id).let { series -> seriesLifecycle.createSeries(series).also { created -> @@ -642,7 +678,7 @@ class BookControllerTest( } @Test - @WithMockCustomUser(roles = ["ADMIN"]) + @WithMockCustomUser(roles = [ROLE_ADMIN]) fun `given json with null fields when updating metadata then fields with null are unset`() { val testDate = LocalDate.of(2020, 1, 1) @@ -699,7 +735,7 @@ class BookControllerTest( } @Test - @WithMockCustomUser(roles = ["ADMIN"]) + @WithMockCustomUser(roles = [ROLE_ADMIN]) fun `given json without fields when updating metadata then existing fields are untouched`() { val testDate = LocalDate.of(2020, 1, 1) diff --git a/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/FileSystemControllerTest.kt b/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/FileSystemControllerTest.kt index c74b681af..08ea3c81f 100644 --- a/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/FileSystemControllerTest.kt +++ b/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/FileSystemControllerTest.kt @@ -1,5 +1,7 @@ package org.gotson.komga.interfaces.rest +import org.gotson.komga.domain.model.ROLE_ADMIN +import org.gotson.komga.domain.model.ROLE_USER import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.springframework.beans.factory.annotation.Autowired @@ -36,7 +38,7 @@ class FileSystemControllerTest( } @Test - @WithMockUser(roles = ["USER", "ADMIN"]) + @WithMockUser(roles = [ROLE_USER, ROLE_ADMIN]) fun `given relative path param when getDirectoryListing then return bad request`() { mockMvc.post(route) { contentType = MediaType.APPLICATION_JSON @@ -45,7 +47,7 @@ class FileSystemControllerTest( } @Test - @WithMockUser(roles = ["USER", "ADMIN"]) + @WithMockUser(roles = [ROLE_USER, ROLE_ADMIN]) fun `given non-existent path param when getDirectoryListing then return bad request`() { val parent = Files.createTempDirectory(null) Files.delete(parent) diff --git a/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/LibraryControllerTest.kt b/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/LibraryControllerTest.kt index 0c0dfd33b..42e882501 100644 --- a/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/LibraryControllerTest.kt +++ b/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/LibraryControllerTest.kt @@ -1,5 +1,7 @@ package org.gotson.komga.interfaces.rest +import org.gotson.komga.domain.model.ROLE_ADMIN +import org.gotson.komga.domain.model.ROLE_USER import org.gotson.komga.domain.model.makeLibrary import org.gotson.komga.domain.persistence.LibraryRepository import org.junit.jupiter.api.AfterAll @@ -70,7 +72,7 @@ class LibraryControllerTest( } @Test - @WithMockUser(roles = ["USER"]) + @WithMockUser(roles = [ROLE_USER]) fun `given user with USER role when addOne then return forbidden`() { val jsonString = """{"name":"test", "root": "C:\\Temp"}""" @@ -114,7 +116,7 @@ class LibraryControllerTest( } @Test - @WithMockCustomUser(roles = ["ADMIN"]) + @WithMockCustomUser(roles = [ROLE_ADMIN]) fun `given admin user when getting books then root is available`() { mockMvc.get(route) .andExpect { diff --git a/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/MockSpringSecurity.kt b/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/MockSpringSecurity.kt index 5f3b930c7..a7afed6a1 100644 --- a/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/MockSpringSecurity.kt +++ b/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/MockSpringSecurity.kt @@ -1,6 +1,9 @@ package org.gotson.komga.interfaces.rest import org.gotson.komga.domain.model.KomgaUser +import org.gotson.komga.domain.model.ROLE_ADMIN +import org.gotson.komga.domain.model.ROLE_FILE_DOWNLOAD +import org.gotson.komga.domain.model.ROLE_PAGE_STREAMING import org.gotson.komga.infrastructure.security.KomgaPrincipal import org.springframework.security.authentication.UsernamePasswordAuthenticationToken import org.springframework.security.core.context.SecurityContext @@ -13,7 +16,7 @@ import org.springframework.security.test.context.support.WithSecurityContextFact @WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory::class, setupBefore = TestExecutionEvent.TEST_EXECUTION) annotation class WithMockCustomUser( val email: String = "user@example.org", - val roles: Array = [], + val roles: Array = [ROLE_FILE_DOWNLOAD, ROLE_PAGE_STREAMING], val sharedAllLibraries: Boolean = true, val sharedLibraries: LongArray = [], val id: Long = 0 @@ -27,7 +30,9 @@ class WithMockCustomUserSecurityContextFactory : WithSecurityContextFactory seriesLifecycle.createSeries(series).also { created -> @@ -366,7 +367,7 @@ class SeriesControllerTest( """{"title":""}""", """{"titleSort":""}""" ]) - @WithMockCustomUser(roles = ["ADMIN"]) + @WithMockCustomUser(roles = [ROLE_ADMIN]) fun `given invalid json when updating metadata then raise validation error`(jsonString: String) { mockMvc.patch("/api/v1/series/1/metadata") { contentType = MediaType.APPLICATION_JSON @@ -377,7 +378,7 @@ class SeriesControllerTest( } @Test - @WithMockCustomUser(roles = ["ADMIN"]) + @WithMockCustomUser(roles = [ROLE_ADMIN]) fun `given valid json when updating metadata then fields are updated`() { val createdSeries = makeSeries(name = "series", libraryId = library.id).let { series -> seriesLifecycle.createSeries(series).also { created -> diff --git a/komga/src/test/resources/application-test.yml b/komga/src/test/resources/application-test.yml index 7d17ef9ff..7493c0d41 100644 --- a/komga/src/test/resources/application-test.yml +++ b/komga/src/test/resources/application-test.yml @@ -1,5 +1,9 @@ application.version: TESTING +komga: + database-backup: + enabled: false + spring: datasource: url: jdbc:h2:mem:testdb