mirror of
https://github.com/gotson/komga.git
synced 2025-12-16 13:33:49 +01:00
feat(api): manage restrictions for users
This commit is contained in:
parent
5ecc9c6785
commit
e345d6f9ef
9 changed files with 329 additions and 22 deletions
|
|
@ -1,35 +1,39 @@
|
|||
package org.gotson.komga.domain.model
|
||||
|
||||
sealed class ContentRestriction {
|
||||
sealed class AgeRestriction(val age: Int) : ContentRestriction() {
|
||||
sealed class AgeRestriction : ContentRestriction() {
|
||||
abstract val age: Int
|
||||
|
||||
/**
|
||||
* Allow only content that has an age rating equal to or under the provided [age]
|
||||
*
|
||||
* @param[age] the age rating to allow
|
||||
*/
|
||||
class AllowOnlyUnder(age: Int) : AgeRestriction(age)
|
||||
data class AllowOnlyUnder(override val age: Int) : AgeRestriction()
|
||||
|
||||
/**
|
||||
* Exclude content that has an age rating equal to or over the provided [age]
|
||||
*
|
||||
* @param[age] the age rating to exclude
|
||||
*/
|
||||
class ExcludeOver(age: Int) : AgeRestriction(age)
|
||||
data class ExcludeOver(override val age: Int) : AgeRestriction()
|
||||
}
|
||||
|
||||
sealed class LabelsRestriction(val labels: Set<String>) : ContentRestriction() {
|
||||
sealed class LabelsRestriction : ContentRestriction() {
|
||||
abstract val labels: Set<String>
|
||||
|
||||
/**
|
||||
* Allow only content that has at least one of the provided sharing [labels]
|
||||
*
|
||||
* @param[labels] a set of sharing labels to allow access to
|
||||
*/
|
||||
class AllowOnly(labels: Set<String>) : LabelsRestriction(labels)
|
||||
data class AllowOnly(override val labels: Set<String>) : LabelsRestriction()
|
||||
|
||||
/**
|
||||
* Exclude content that has at least one of the provided sharing [labels]
|
||||
*
|
||||
* @param[labels] a set of sharing labels to exclude
|
||||
*/
|
||||
class Exclude(labels: Set<String>) : LabelsRestriction(labels)
|
||||
data class Exclude(override val labels: Set<String>) : LabelsRestriction()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ package org.gotson.komga.domain.model
|
|||
|
||||
import org.gotson.komga.language.lowerNotBlank
|
||||
import org.gotson.komga.language.toSetOrNull
|
||||
import java.io.Serializable
|
||||
|
||||
class ContentRestrictions(
|
||||
val ageRestriction: ContentRestriction.AgeRestriction? = null,
|
||||
labelsAllow: Set<String> = emptySet(),
|
||||
labelsExclude: Set<String> = emptySet(),
|
||||
) {
|
||||
) : Serializable {
|
||||
val labelsAllowRestriction =
|
||||
labelsAllow.lowerNotBlank().toSet()
|
||||
.minus(labelsExclude.lowerNotBlank().toSet())
|
||||
|
|
@ -19,4 +20,22 @@ class ContentRestrictions(
|
|||
fun isRestricted() = ageRestriction != null || labelsAllowRestriction != null || labelsExcludeRestriction != null
|
||||
|
||||
override fun toString(): String = "ContentRestriction(ageRestriction=$ageRestriction, labelsAllowRestriction=$labelsAllowRestriction, labelsExcludeRestriction=$labelsExcludeRestriction)"
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is ContentRestrictions) return false
|
||||
|
||||
if (ageRestriction != other.ageRestriction) return false
|
||||
if (labelsAllowRestriction != other.labelsAllowRestriction) return false
|
||||
if (labelsExcludeRestriction != other.labelsExcludeRestriction) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = ageRestriction?.hashCode() ?: 0
|
||||
result = 31 * result + (labelsAllowRestriction?.hashCode() ?: 0)
|
||||
result = 31 * result + (labelsExcludeRestriction?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,4 +43,7 @@ sealed class DomainEvent : Serializable {
|
|||
|
||||
data class ThumbnailReadListAdded(val thumbnail: ThumbnailReadList) : DomainEvent()
|
||||
data class ThumbnailReadListDeleted(val thumbnail: ThumbnailReadList) : DomainEvent()
|
||||
|
||||
data class UserUpdated(val user: KomgaUser, val expireSession: Boolean) : DomainEvent()
|
||||
data class UserDeleted(val user: KomgaUser) : DomainEvent()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package org.gotson.komga.domain.service
|
||||
|
||||
import mu.KotlinLogging
|
||||
import org.gotson.komga.application.events.EventPublisher
|
||||
import org.gotson.komga.domain.model.DomainEvent
|
||||
import org.gotson.komga.domain.model.KomgaUser
|
||||
import org.gotson.komga.domain.model.UserEmailAlreadyExistsException
|
||||
import org.gotson.komga.domain.persistence.AuthenticationActivityRepository
|
||||
|
|
@ -22,7 +24,7 @@ class KomgaUserLifecycle(
|
|||
private val passwordEncoder: PasswordEncoder,
|
||||
private val sessionRegistry: SessionRegistry,
|
||||
private val transactionTemplate: TransactionTemplate,
|
||||
|
||||
private val eventPublisher: EventPublisher,
|
||||
) {
|
||||
|
||||
fun updatePassword(user: KomgaUser, newPassword: String, expireSessions: Boolean) {
|
||||
|
|
@ -31,6 +33,26 @@ class KomgaUserLifecycle(
|
|||
userRepository.update(updatedUser)
|
||||
|
||||
if (expireSessions) expireSessions(updatedUser)
|
||||
|
||||
eventPublisher.publishEvent(DomainEvent.UserUpdated(updatedUser, expireSessions))
|
||||
}
|
||||
|
||||
fun updateUser(user: KomgaUser) {
|
||||
val existing = userRepository.findByIdOrNull(user.id)
|
||||
requireNotNull(existing) { "User doesn't exist, cannot update: $user" }
|
||||
|
||||
val toUpdate = user.copy(password = existing.password)
|
||||
logger.info { "Update user: $toUpdate" }
|
||||
userRepository.update(toUpdate)
|
||||
|
||||
val expireSessions = existing.roles() != user.roles() ||
|
||||
existing.restrictions != user.restrictions ||
|
||||
existing.sharedAllLibraries != user.sharedAllLibraries ||
|
||||
existing.sharedLibrariesIds != user.sharedLibrariesIds
|
||||
|
||||
if (expireSessions) expireSessions(toUpdate)
|
||||
|
||||
eventPublisher.publishEvent(DomainEvent.UserUpdated(toUpdate, expireSessions))
|
||||
}
|
||||
|
||||
fun countUsers() = userRepository.count()
|
||||
|
|
@ -56,9 +78,11 @@ class KomgaUserLifecycle(
|
|||
}
|
||||
|
||||
expireSessions(user)
|
||||
|
||||
eventPublisher.publishEvent(DomainEvent.UserUpdated(user, true))
|
||||
}
|
||||
|
||||
private fun expireSessions(user: KomgaUser) {
|
||||
fun expireSessions(user: KomgaUser) {
|
||||
logger.info { "Expiring all sessions for user: ${user.email}" }
|
||||
sessionRegistry
|
||||
.getAllSessions(KomgaPrincipal(user), false)
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ class UserV1Controller(
|
|||
roleFileDownload = patch.roles.contains(ROLE_FILE_DOWNLOAD),
|
||||
rolePageStreaming = patch.roles.contains(ROLE_PAGE_STREAMING),
|
||||
)
|
||||
userRepository.update(updatedUser)
|
||||
userLifecycle.updateUser(updatedUser)
|
||||
logger.info { "Updated user roles: $updatedUser" }
|
||||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
}
|
||||
|
|
@ -160,7 +160,7 @@ class UserV1Controller(
|
|||
.map { it.id }
|
||||
.toSet(),
|
||||
)
|
||||
userRepository.update(updatedUser)
|
||||
userLifecycle.updateUser(updatedUser)
|
||||
logger.info { "Updated user shared libraries: $updatedUser" }
|
||||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package org.gotson.komga.interfaces.api.rest
|
|||
|
||||
import io.swagger.v3.oas.annotations.Parameter
|
||||
import mu.KotlinLogging
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestrictions
|
||||
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
|
||||
|
|
@ -12,6 +14,7 @@ import org.gotson.komga.domain.persistence.LibraryRepository
|
|||
import org.gotson.komga.domain.service.KomgaUserLifecycle
|
||||
import org.gotson.komga.infrastructure.jooq.UnpagedSorted
|
||||
import org.gotson.komga.infrastructure.security.KomgaPrincipal
|
||||
import org.gotson.komga.interfaces.api.rest.dto.AllowExclude
|
||||
import org.gotson.komga.interfaces.api.rest.dto.AuthenticationActivityDto
|
||||
import org.gotson.komga.interfaces.api.rest.dto.PasswordUpdateDto
|
||||
import org.gotson.komga.interfaces.api.rest.dto.UserCreationDto
|
||||
|
|
@ -118,10 +121,22 @@ class UserV2Controller(
|
|||
if (sharedLibraries!!.all) emptySet()
|
||||
else libraryRepository.findAllByIds(sharedLibraries!!.libraryIds).map { it.id }.toSet()
|
||||
} else existing.sharedLibrariesIds,
|
||||
restrictions = ContentRestrictions(
|
||||
ageRestriction = if (isSet("ageRestriction")) {
|
||||
if (ageRestriction == null) null
|
||||
else when (ageRestriction!!.restriction) {
|
||||
AllowExclude.ALLOW_ONLY -> ContentRestriction.AgeRestriction.AllowOnlyUnder(ageRestriction!!.age)
|
||||
AllowExclude.EXCLUDE -> ContentRestriction.AgeRestriction.ExcludeOver(ageRestriction!!.age)
|
||||
}
|
||||
} else existing.restrictions.ageRestriction,
|
||||
labelsAllow = if (isSet("labelsAllow")) labelsAllow
|
||||
?: emptySet() else existing.restrictions.labelsAllowRestriction?.labels ?: emptySet(),
|
||||
labelsExclude = if (isSet("labelsExclude")) labelsExclude
|
||||
?: emptySet() else existing.restrictions.labelsExcludeRestriction?.labels ?: emptySet(),
|
||||
),
|
||||
)
|
||||
}
|
||||
userRepository.update(updatedUser)
|
||||
logger.info { "Updated user: $updatedUser" }
|
||||
userLifecycle.updateUser(updatedUser)
|
||||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package org.gotson.komga.interfaces.api.rest.dto
|
||||
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.KomgaUser
|
||||
import org.gotson.komga.domain.model.ROLE_ADMIN
|
||||
import org.gotson.komga.domain.model.ROLE_FILE_DOWNLOAD
|
||||
|
|
@ -32,8 +35,20 @@ data class UserDtoV2(
|
|||
val roles: Set<String>,
|
||||
val sharedAllLibraries: Boolean,
|
||||
val sharedLibrariesIds: Set<String>,
|
||||
val labelsAllow: Set<String>,
|
||||
val labelsExclude: Set<String>,
|
||||
val ageRestriction: AgeRestrictionDto?,
|
||||
)
|
||||
|
||||
data class AgeRestrictionDto(
|
||||
val age: Int,
|
||||
val restriction: AllowExclude,
|
||||
)
|
||||
|
||||
enum class AllowExclude {
|
||||
ALLOW_ONLY, EXCLUDE,
|
||||
}
|
||||
|
||||
fun KomgaUser.toDtoV2() =
|
||||
UserDtoV2(
|
||||
id = id,
|
||||
|
|
@ -41,6 +56,13 @@ fun KomgaUser.toDtoV2() =
|
|||
roles = roles(),
|
||||
sharedAllLibraries = sharedAllLibraries,
|
||||
sharedLibrariesIds = sharedLibrariesIds,
|
||||
labelsAllow = restrictions.labelsAllowRestriction?.labels ?: emptySet(),
|
||||
labelsExclude = restrictions.labelsExcludeRestriction?.labels ?: emptySet(),
|
||||
ageRestriction = when (restrictions.ageRestriction) {
|
||||
is ContentRestriction.AgeRestriction.AllowOnlyUnder -> AgeRestrictionDto(restrictions.ageRestriction.age, AllowExclude.ALLOW_ONLY)
|
||||
is ContentRestriction.AgeRestriction.ExcludeOver -> AgeRestrictionDto(restrictions.ageRestriction.age, AllowExclude.EXCLUDE)
|
||||
null -> null
|
||||
}
|
||||
)
|
||||
|
||||
fun KomgaPrincipal.toDtoV2() = user.toDtoV2()
|
||||
|
|
|
|||
|
|
@ -1,19 +1,28 @@
|
|||
package org.gotson.komga.interfaces.api.rest.dto
|
||||
|
||||
import javax.validation.Valid
|
||||
import javax.validation.constraints.PositiveOrZero
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class UserUpdateDto {
|
||||
private val isSet = mutableMapOf<String, Boolean>()
|
||||
fun isSet(prop: String) = isSet.getOrDefault(prop, false)
|
||||
|
||||
// @get:NullOrNotBlank
|
||||
// val title: String? = null
|
||||
@get:Valid
|
||||
var ageRestriction: AgeRestrictionUpdateDto?
|
||||
by Delegates.observable(null) { prop, _, _ ->
|
||||
isSet[prop.name] = true
|
||||
}
|
||||
|
||||
// @get:PositiveOrZero
|
||||
// var ageRating: Int?
|
||||
// by Delegates.observable(null) { prop, _, _ ->
|
||||
// isSet[prop.name] = true
|
||||
// }
|
||||
var labelsAllow: Set<String>?
|
||||
by Delegates.observable(null) { prop, _, _ ->
|
||||
isSet[prop.name] = true
|
||||
}
|
||||
|
||||
var labelsExclude: Set<String>?
|
||||
by Delegates.observable(null) { prop, _, _ ->
|
||||
isSet[prop.name] = true
|
||||
}
|
||||
|
||||
var roles: Set<String>?
|
||||
by Delegates.observable(null) { prop, _, _ ->
|
||||
|
|
@ -26,6 +35,12 @@ class UserUpdateDto {
|
|||
}
|
||||
}
|
||||
|
||||
data class AgeRestrictionUpdateDto(
|
||||
@get:PositiveOrZero
|
||||
val age: Int,
|
||||
val restriction: AllowExclude,
|
||||
)
|
||||
|
||||
data class SharedLibrariesUpdateDto(
|
||||
val all: Boolean,
|
||||
val libraryIds: Set<String>,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
package org.gotson.komga.interfaces.api.rest
|
||||
|
||||
import com.ninjasquad.springmockk.SpykBean
|
||||
import io.mockk.verify
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestrictions
|
||||
import org.gotson.komga.domain.model.KomgaUser
|
||||
import org.gotson.komga.domain.model.ROLE_ADMIN
|
||||
import org.gotson.komga.domain.model.ROLE_FILE_DOWNLOAD
|
||||
|
|
@ -37,8 +41,11 @@ class UserControllerTest(
|
|||
@Autowired private val libraryRepository: LibraryRepository,
|
||||
@Autowired private val libraryLifecycle: LibraryLifecycle,
|
||||
@Autowired private val userRepository: KomgaUserRepository,
|
||||
@Autowired private val userLifecycle: KomgaUserLifecycle,
|
||||
) {
|
||||
|
||||
@SpykBean
|
||||
private lateinit var userLifecycle: KomgaUserLifecycle
|
||||
|
||||
private val admin = KomgaUser("admin@example.org", "", true, id = "admin")
|
||||
|
||||
@BeforeAll
|
||||
|
|
@ -106,6 +113,8 @@ class UserControllerTest(
|
|||
assertThat(this.rolePageStreaming).isTrue
|
||||
assertThat(this.roleAdmin).isFalse
|
||||
}
|
||||
|
||||
verify(exactly = 1) { userLifecycle.expireSessions(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -133,6 +142,8 @@ class UserControllerTest(
|
|||
assertThat(this.rolePageStreaming).isFalse
|
||||
assertThat(this.roleAdmin).isFalse
|
||||
}
|
||||
|
||||
verify(exactly = 1) { userLifecycle.expireSessions(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -162,6 +173,8 @@ class UserControllerTest(
|
|||
assertThat(this!!.sharedAllLibraries).isFalse
|
||||
assertThat(this.sharedLibrariesIds).containsExactlyInAnyOrder("1", "2")
|
||||
}
|
||||
|
||||
verify(exactly = 1) { userLifecycle.expireSessions(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -191,11 +204,13 @@ class UserControllerTest(
|
|||
assertThat(this!!.sharedAllLibraries).isFalse
|
||||
assertThat(this.sharedLibrariesIds).containsExactlyInAnyOrder("2")
|
||||
}
|
||||
|
||||
verify(exactly = 1) { userLifecycle.expireSessions(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(id = "admin", roles = [ROLE_ADMIN])
|
||||
fun `given user with library restrictions when removing restrictions then they restrictions are updated`() {
|
||||
fun `given user with library restrictions when removing restrictions then the restrictions are updated`() {
|
||||
val user = KomgaUser("user@example.org", "", false, id = "user", sharedAllLibraries = false, sharedLibrariesIds = setOf("2"))
|
||||
userLifecycle.createUser(user)
|
||||
|
||||
|
|
@ -220,6 +235,196 @@ class UserControllerTest(
|
|||
assertThat(this!!.sharedAllLibraries).isTrue
|
||||
assertThat(this.sharedLibrariesIds).isEmpty()
|
||||
}
|
||||
|
||||
verify(exactly = 1) { userLifecycle.expireSessions(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(id = "admin", roles = [ROLE_ADMIN])
|
||||
fun `given user without labels restrictions when adding restrictions then restrictions are updated`() {
|
||||
val user = KomgaUser("user@example.org", "", false, id = "user")
|
||||
userLifecycle.createUser(user)
|
||||
|
||||
val jsonString = """
|
||||
{
|
||||
"labelsAllow": ["cute", "kids"],
|
||||
"labelsExclude": ["adult"]
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
mockMvc.patch("/api/v2/users/${user.id}") {
|
||||
contentType = MediaType.APPLICATION_JSON
|
||||
content = jsonString
|
||||
}.andExpect {
|
||||
status { isNoContent() }
|
||||
}
|
||||
|
||||
with(userRepository.findByIdOrNull(user.id)) {
|
||||
assertThat(this).isNotNull
|
||||
assertThat(this!!.restrictions.labelsAllowRestriction!!.labels).containsExactlyInAnyOrder("cute", "kids")
|
||||
assertThat(this.restrictions.labelsExcludeRestriction!!.labels).containsOnly("adult")
|
||||
}
|
||||
|
||||
verify(exactly = 1) { userLifecycle.expireSessions(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(id = "admin", roles = [ROLE_ADMIN])
|
||||
fun `given user with labels restrictions when removing restrictions then restrictions are updated`() {
|
||||
val user = KomgaUser(
|
||||
"user@example.org", "", false, id = "user",
|
||||
restrictions = ContentRestrictions(
|
||||
labelsAllow = setOf("kids", "cute"),
|
||||
labelsExclude = setOf("adult"),
|
||||
),
|
||||
)
|
||||
userLifecycle.createUser(user)
|
||||
|
||||
val jsonString = """
|
||||
{
|
||||
"labelsAllow": [],
|
||||
"labelsExclude": null
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
mockMvc.patch("/api/v2/users/${user.id}") {
|
||||
contentType = MediaType.APPLICATION_JSON
|
||||
content = jsonString
|
||||
}.andExpect {
|
||||
status { isNoContent() }
|
||||
}
|
||||
|
||||
with(userRepository.findByIdOrNull(user.id)) {
|
||||
assertThat(this).isNotNull
|
||||
assertThat(this!!.restrictions.labelsAllowRestriction).isNull()
|
||||
assertThat(this.restrictions.labelsExcludeRestriction).isNull()
|
||||
}
|
||||
|
||||
verify(exactly = 1) { userLifecycle.expireSessions(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(id = "admin", roles = [ROLE_ADMIN])
|
||||
fun `given user without age restriction when adding restrictions then restrictions are updated`() {
|
||||
val user = KomgaUser("user@example.org", "", false, id = "user")
|
||||
userLifecycle.createUser(user)
|
||||
|
||||
val jsonString = """
|
||||
{
|
||||
"ageRestriction": {
|
||||
"age": 12,
|
||||
"restriction": "ALLOW_ONLY"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
mockMvc.patch("/api/v2/users/${user.id}") {
|
||||
contentType = MediaType.APPLICATION_JSON
|
||||
content = jsonString
|
||||
}.andExpect {
|
||||
status { isNoContent() }
|
||||
}
|
||||
|
||||
with(userRepository.findByIdOrNull(user.id)) {
|
||||
assertThat(this).isNotNull
|
||||
assertThat(this!!.restrictions.ageRestriction).isNotNull
|
||||
assertThat(this.restrictions.ageRestriction).isInstanceOf(ContentRestriction.AgeRestriction.AllowOnlyUnder::class.java)
|
||||
assertThat(this.restrictions.ageRestriction!!.age).isEqualTo(12)
|
||||
}
|
||||
|
||||
verify(exactly = 1) { userLifecycle.expireSessions(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(id = "admin", roles = [ROLE_ADMIN])
|
||||
fun `given user without age restriction when adding incorrect restrictions then bad request`() {
|
||||
val user = KomgaUser("user@example.org", "", false, id = "user")
|
||||
userLifecycle.createUser(user)
|
||||
|
||||
val jsonString = """
|
||||
{
|
||||
"ageRestriction": {
|
||||
"age": -12,
|
||||
"restriction": "ALLOW_ONLY"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
mockMvc.patch("/api/v2/users/${user.id}") {
|
||||
contentType = MediaType.APPLICATION_JSON
|
||||
content = jsonString
|
||||
}.andExpect {
|
||||
status { isBadRequest() }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(id = "admin", roles = [ROLE_ADMIN])
|
||||
fun `given user with age restriction when removing restriction then restrictions are updated`() {
|
||||
val user = KomgaUser(
|
||||
"user@example.org", "", false, id = "user",
|
||||
restrictions = ContentRestrictions(
|
||||
ageRestriction = ContentRestriction.AgeRestriction.AllowOnlyUnder(12),
|
||||
),
|
||||
)
|
||||
userLifecycle.createUser(user)
|
||||
|
||||
val jsonString = """
|
||||
{
|
||||
"ageRestriction": null
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
mockMvc.patch("/api/v2/users/${user.id}") {
|
||||
contentType = MediaType.APPLICATION_JSON
|
||||
content = jsonString
|
||||
}.andExpect {
|
||||
status { isNoContent() }
|
||||
}
|
||||
|
||||
with(userRepository.findByIdOrNull(user.id)) {
|
||||
assertThat(this).isNotNull
|
||||
assertThat(this!!.restrictions.ageRestriction).isNull()
|
||||
}
|
||||
|
||||
verify(exactly = 1) { userLifecycle.expireSessions(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(id = "admin", roles = [ROLE_ADMIN])
|
||||
fun `given user with age restriction when changing restriction then restrictions are updated`() {
|
||||
val user = KomgaUser(
|
||||
"user@example.org", "", false, id = "user",
|
||||
restrictions = ContentRestrictions(
|
||||
ageRestriction = ContentRestriction.AgeRestriction.AllowOnlyUnder(12),
|
||||
),
|
||||
)
|
||||
userLifecycle.createUser(user)
|
||||
|
||||
val jsonString = """
|
||||
{
|
||||
"ageRestriction": {
|
||||
"age": 16,
|
||||
"restriction": "EXCLUDE"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
mockMvc.patch("/api/v2/users/${user.id}") {
|
||||
contentType = MediaType.APPLICATION_JSON
|
||||
content = jsonString
|
||||
}.andExpect {
|
||||
status { isNoContent() }
|
||||
}
|
||||
|
||||
with(userRepository.findByIdOrNull(user.id)) {
|
||||
assertThat(this).isNotNull
|
||||
assertThat(this!!.restrictions.ageRestriction).isNotNull
|
||||
assertThat(this.restrictions.ageRestriction).isExactlyInstanceOf(ContentRestriction.AgeRestriction.ExcludeOver::class.java)
|
||||
assertThat(this.restrictions.ageRestriction!!.age).isEqualTo(16)
|
||||
}
|
||||
|
||||
verify(exactly = 1) { userLifecycle.expireSessions(any()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue