#42 Improve VoteVoter

This commit is contained in:
2020-03-17 15:48:59 +01:00
parent 4b96080051
commit cf2881c890
5 changed files with 108 additions and 25 deletions

View File

@@ -2,7 +2,6 @@ package fr.dcproject.security.voter
import fr.dcproject.entity.ArticleAuthI import fr.dcproject.entity.ArticleAuthI
import fr.dcproject.entity.ArticleI import fr.dcproject.entity.ArticleI
import fr.dcproject.entity.ArticleSimpleI
import fr.dcproject.entity.UserI import fr.dcproject.entity.UserI
import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCall
import fr.dcproject.entity.Comment as CommentEntity import fr.dcproject.entity.Comment as CommentEntity
@@ -27,7 +26,7 @@ class ArticleVoter : Voter {
if (action == Action.VIEW) return view(subject, user) if (action == Action.VIEW) return view(subject, user)
if (action == Action.DELETE) return delete(subject, user) if (action == Action.DELETE) return delete(subject, user)
if (action == Action.UPDATE) return update(subject, user) if (action == Action.UPDATE) return update(subject, user)
if (action is CommentVoter.Action) return voteForComment(action) if (action is CommentVoter.Action) return voteForComment(action, subject)
if (action is VoteVoter.Action) return voteForVote(action, subject) if (action is VoteVoter.Action) return voteForVote(action, subject)
if (action is Action) return Vote.DENIED if (action is Action) return Vote.DENIED
@@ -67,17 +66,27 @@ class ArticleVoter : Voter {
private fun voteForVote(action: VoteVoter.Action, subject: Any?): Vote { private fun voteForVote(action: VoteVoter.Action, subject: Any?): Vote {
if (action == VoteVoter.Action.CREATE && subject is VoteEntity<*>) { if (action == VoteVoter.Action.CREATE && subject is VoteEntity<*>) {
val target = subject.target val target = subject.target
if (target !is ArticleSimpleI) { if (target is ArticleAuthI<*>) {
return Vote.ABSTAIN
}
if (target.isDeleted()) { if (target.isDeleted()) {
return Vote.DENIED return Vote.DENIED
} }
} else if (target is ArticleI) {
return Vote.DENIED
}
} }
return Vote.ABSTAIN return Vote.ABSTAIN
} }
private fun voteForComment(action: CommentVoter.Action): Vote { private fun voteForComment(action: CommentVoter.Action, subject: Any?): Vote {
if (subject is CommentEntity<*>) {
val target = subject.target
if (target is ArticleAuthI<*>) {
if (target.isDeleted()) {
return Vote.DENIED
}
} else if (target is ArticleI) {
return Vote.DENIED
}
if (action == CommentVoter.Action.CREATE) { if (action == CommentVoter.Action.CREATE) {
return Vote.GRANTED return Vote.GRANTED
} }
@@ -85,6 +94,9 @@ class ArticleVoter : Voter {
if (action == CommentVoter.Action.VIEW) { if (action == CommentVoter.Action.VIEW) {
return Vote.GRANTED return Vote.GRANTED
} }
} else {
return Vote.DENIED
}
return Vote.ABSTAIN return Vote.ABSTAIN
} }

View File

@@ -16,7 +16,7 @@ class ConstitutionVoter : Voter {
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean { override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return (action is Action || action is CommentVoter.Action || action is VoteVoter.Action) return (action is Action || action is CommentVoter.Action || action is VoteVoter.Action)
.and(subject is List<*> || subject is ConstitutionSimple<*, *>? || subject is VoteEntity<*> || subject is Comment<*>) .and(subject is ConstitutionSimple<*, *>? || subject is VoteEntity<*> || subject is Comment<*>)
} }
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote { override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
@@ -30,14 +30,6 @@ class ConstitutionVoter : Voter {
return if (subject.isDeleted()) Vote.DENIED return if (subject.isDeleted()) Vote.DENIED
else Vote.GRANTED else Vote.GRANTED
} }
if (subject is List<*>) {
subject.forEach {
if (it !is ConstitutionSimple<*, *> || it.isDeleted()) {
return Vote.DENIED
}
}
return Vote.GRANTED
}
return Vote.DENIED return Vote.DENIED
} }

View File

@@ -48,6 +48,23 @@ internal class CommentVoterTest {
target = article1 target = article1
) )
private val commentTargetDeleted = Comment(
content = "Hello",
createdBy = tesla,
target = Article(
content = "Hi",
createdBy = einstein,
description = "blablabla",
title = "Super article"
).apply { deletedAt = DateTime.now() }
)
private val commentTargetNoUser = Comment(
content = "Hello",
createdBy = tesla,
target = ArticleRef()
)
init { init {
mockkStatic("fr.dcproject.security.voter.VoterKt") mockkStatic("fr.dcproject.security.voter.VoterKt")
} }
@@ -65,7 +82,7 @@ internal class CommentVoterTest {
} }
@Test @Test
fun `can be view the comment`() = listOf(CommentVoter()).run { fun `can be view the comment`() = listOf(CommentVoter(), ArticleVoter()).run {
mockk<ApplicationCall> { mockk<ApplicationCall> {
every { user } returns tesla.user every { user } returns tesla.user
}.let { }.let {
@@ -110,7 +127,7 @@ internal class CommentVoterTest {
} }
@Test @Test
fun `can be create a comment`() = listOf(CommentVoter()).run { fun `can be create a comment`() = listOf(CommentVoter(), ArticleVoter()).run {
mockk<ApplicationCall> { mockk<ApplicationCall> {
every { user } returns tesla.user every { user } returns tesla.user
}.let { }.let {
@@ -118,6 +135,24 @@ internal class CommentVoterTest {
} }
} }
@Test
fun `can not be create a comment if target is deleted`() = listOf(CommentVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(CommentVoter.Action.CREATE, it, commentTargetDeleted) `should be` false
}
}
@Test
fun `can not be create a comment if target has no user`() = listOf(CommentVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(CommentVoter.Action.CREATE, it, commentTargetNoUser) `should be` false
}
}
@Test @Test
fun `can not be create a comment with other creator`() = listOf(CommentVoter()).run { fun `can not be create a comment with other creator`() = listOf(CommentVoter()).run {
mockk<ApplicationCall> { mockk<ApplicationCall> {

View File

@@ -52,6 +52,23 @@ internal class VoteVoterTest {
note = 1 note = 1
) )
private val voteOnDeleted = VoteEntity(
createdBy = tesla,
target = Article(
content = "Hi",
createdBy = einstein,
description = "blablabla",
title = "Super article"
).apply { deletedAt = DateTime.now() },
note = 1
)
private val voteWithoutUser = VoteEntity(
createdBy = tesla,
target = ArticleRef(),
note = 1
)
init { init {
mockkStatic("fr.dcproject.security.voter.VoterKt") mockkStatic("fr.dcproject.security.voter.VoterKt")
} }
@@ -105,7 +122,7 @@ internal class VoteVoterTest {
} }
@Test @Test
fun `can be vote an article`() = listOf(VoteVoter()).run { fun `can be vote an article`() = listOf(VoteVoter(), ArticleVoter()).run {
mockk<ApplicationCall> { mockk<ApplicationCall> {
every { user } returns tesla.user every { user } returns tesla.user
}.let { }.let {
@@ -121,4 +138,31 @@ internal class VoteVoterTest {
can(VoteVoter.Action.CREATE, it, vote1) `should be` false can(VoteVoter.Action.CREATE, it, vote1) `should be` false
} }
} }
@Test
fun `can not be vote an article if article is deleted`() = listOf(VoteVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(VoteVoter.Action.CREATE, it, voteOnDeleted) `should be` false
}
}
@Test
fun `can not be vote an article if article have no user`() = listOf(VoteVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(VoteVoter.Action.CREATE, it, voteWithoutUser) `should be` false
}
}
@Test
fun `can not be comment an article if article is deleted`() = listOf(VoteVoter(), ArticleVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(CommentVoter.Action.CREATE, it, voteOnDeleted) `should be` false
}
}
} }