mirror of
https://github.com/gotson/komga.git
synced 2025-01-09 04:08:00 +08:00
feat(api): restrict page streaming and file download with roles
also add the ability to edit user roles related to #146
This commit is contained in:
parent
327ed00857
commit
6291dab864
@ -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;
|
@ -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<Long> = emptySet(),
|
||||
val sharedAllLibraries: Boolean = true,
|
||||
val id: Long = 0,
|
||||
@ -19,8 +26,10 @@ data class KomgaUser(
|
||||
) : Auditable() {
|
||||
|
||||
fun roles(): Set<String> {
|
||||
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)"
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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.")
|
||||
|
@ -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 {
|
||||
|
@ -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.")
|
||||
|
@ -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<UserWithSharedLibrariesDto> =
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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<String> = 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<Long>
|
||||
)
|
||||
|
||||
data class RolesUpdateDto(
|
||||
val roles: List<String>
|
||||
)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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<String> = [],
|
||||
val roles: Array<String> = [ROLE_FILE_DOWNLOAD, ROLE_PAGE_STREAMING],
|
||||
val sharedAllLibraries: Boolean = true,
|
||||
val sharedLibraries: LongArray = [],
|
||||
val id: Long = 0
|
||||
@ -27,7 +30,9 @@ class WithMockCustomUserSecurityContextFactory : WithSecurityContextFactory<With
|
||||
KomgaUser(
|
||||
email = customUser.email,
|
||||
password = "",
|
||||
roleAdmin = customUser.roles.contains("ADMIN"),
|
||||
roleAdmin = customUser.roles.contains(ROLE_ADMIN),
|
||||
roleFileDownload = customUser.roles.contains(ROLE_FILE_DOWNLOAD),
|
||||
rolePageStreaming = customUser.roles.contains(ROLE_PAGE_STREAMING),
|
||||
sharedAllLibraries = customUser.sharedAllLibraries,
|
||||
sharedLibrariesIds = customUser.sharedLibraries.toSet(),
|
||||
id = customUser.id
|
||||
|
@ -4,6 +4,7 @@ import org.assertj.core.api.Assertions.assertThat
|
||||
import org.gotson.komga.domain.model.BookPage
|
||||
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.SeriesMetadata
|
||||
import org.gotson.komga.domain.model.makeBook
|
||||
import org.gotson.komga.domain.model.makeLibrary
|
||||
@ -316,7 +317,7 @@ class SeriesControllerTest(
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(roles = ["ADMIN"])
|
||||
@WithMockCustomUser(roles = [ROLE_ADMIN])
|
||||
fun `given admin user when getting series then url is available`() {
|
||||
val createdSeries = makeSeries(name = "series", libraryId = library.id).let { series ->
|
||||
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 ->
|
||||
|
@ -1,5 +1,9 @@
|
||||
application.version: TESTING
|
||||
|
||||
komga:
|
||||
database-backup:
|
||||
enabled: false
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
|
Loading…
Reference in New Issue
Block a user