Refactoring of OpinionChoiceVoter

This commit is contained in:
2021-01-18 13:03:01 +01:00
parent ba673943d8
commit 64fa0912b8
5 changed files with 30 additions and 74 deletions

View File

@@ -41,9 +41,7 @@ import fr.dcproject.component.workgroup.routes.members.UpdateMemberOfWorkgroup.u
import fr.dcproject.event.EventNotification import fr.dcproject.event.EventNotification
import fr.dcproject.event.EventSubscriber import fr.dcproject.event.EventSubscriber
import fr.dcproject.routes.* import fr.dcproject.routes.*
import fr.dcproject.security.voter.OpinionChoiceVoter import fr.dcproject.voter.VoterDeniedException
import fr.ktorVoter.AuthorizationVoter
import fr.ktorVoter.VoterException
import fr.postgresjson.migration.Migrations import fr.postgresjson.migration.Migrations
import io.ktor.application.* import io.ktor.application.*
import io.ktor.auth.* import io.ktor.auth.*
@@ -89,12 +87,6 @@ fun Application.module(env: Env = PROD) {
install(Locations) install(Locations)
install(AuthorizationVoter) {
voters = listOf(
OpinionChoiceVoter()
)
}
HttpClient(Jetty) { HttpClient(Jetty) {
engine { engine {
} }
@@ -176,7 +168,7 @@ fun Application.module(env: Env = PROD) {
voteArticle(get(), get(), get(), get()) voteArticle(get(), get(), get(), get())
voteConstitution(get(), get()) voteConstitution(get(), get())
opinionArticle(get(), get()) opinionArticle(get(), get())
opinionChoice(get()) opinionChoice(get(), get())
definition() definition()
} }
@@ -198,7 +190,7 @@ fun Application.module(env: Env = PROD) {
exception<NotFoundException> { e -> exception<NotFoundException> { e ->
call.respond(HttpStatusCode.NotFound, e.message!!) call.respond(HttpStatusCode.NotFound, e.message!!)
} }
exception<VoterException> { exception<VoterDeniedException> {
if (call.user == null) call.respond(HttpStatusCode.Unauthorized) if (call.user == null) call.respond(HttpStatusCode.Unauthorized)
else call.respond(HttpStatusCode.Forbidden) else call.respond(HttpStatusCode.Forbidden)
} }

View File

@@ -23,10 +23,7 @@ import fr.dcproject.event.publisher.Publisher
import fr.dcproject.messages.Mailer import fr.dcproject.messages.Mailer
import fr.dcproject.messages.NotificationEmailSender import fr.dcproject.messages.NotificationEmailSender
import fr.dcproject.repository.CommentConstitutionRepository import fr.dcproject.repository.CommentConstitutionRepository
import fr.dcproject.security.voter.ConstitutionVoter import fr.dcproject.security.voter.*
import fr.dcproject.security.voter.FollowVoter
import fr.dcproject.security.voter.OpinionVoter
import fr.dcproject.security.voter.VoteVoter
import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Connection
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.migration.Migrations import fr.postgresjson.migration.Migrations
@@ -131,6 +128,7 @@ val KoinModule = module {
single { VoteVoter() } single { VoteVoter() }
single { FollowVoter() } single { FollowVoter() }
single { OpinionVoter() } single { OpinionVoter() }
single { OpinionChoiceVoter() }
// Elasticsearch Client // Elasticsearch Client
single<RestClient> { single<RestClient> {

View File

@@ -1,9 +1,9 @@
package fr.dcproject.routes package fr.dcproject.routes
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.entity.OpinionChoice import fr.dcproject.entity.OpinionChoice
import fr.dcproject.security.voter.OpinionChoiceVoter.Action.VIEW import fr.dcproject.security.voter.OpinionChoiceVoter
import fr.ktorVoter.assertCan import fr.dcproject.voter.assert
import fr.ktorVoter.assertCanAll
import io.ktor.application.* import io.ktor.application.*
import io.ktor.locations.* import io.ktor.locations.*
import io.ktor.response.* import io.ktor.response.*
@@ -20,17 +20,17 @@ object OpinionChoicePaths {
} }
@KtorExperimentalLocationsAPI @KtorExperimentalLocationsAPI
fun Route.opinionChoice(repo: OpinionChoiceRepository) { fun Route.opinionChoice(repo: OpinionChoiceRepository, voter: OpinionChoiceVoter) {
get<OpinionChoicePaths.OpinionChoiceRequest> { get<OpinionChoicePaths.OpinionChoiceRequest> {
assertCan(VIEW, it.opinionChoice) voter.assert { canView(it.opinionChoice, citizenOrNull) }
call.respond(it.opinionChoice) call.respond(it.opinionChoice)
} }
get<OpinionChoicePaths.OpinionChoicesRequest> { get<OpinionChoicePaths.OpinionChoicesRequest> {
val opinions = repo.findOpinionsChoices(it.targets) val opinionChoices = repo.findOpinionsChoices(it.targets)
assertCanAll(VIEW, opinions) voter.assert { canView(opinionChoices, citizenOrNull) }
call.respond(opinions) call.respond(opinionChoices)
} }
} }

View File

@@ -1,26 +1,15 @@
package fr.dcproject.security.voter package fr.dcproject.security.voter
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.OpinionChoice import fr.dcproject.entity.OpinionChoice
import fr.dcproject.voter.NoSubjectDefinedException import fr.dcproject.voter.Voter
import fr.ktorVoter.* import fr.dcproject.voter.VoterResponse
import io.ktor.application.*
class OpinionChoiceVoter : Voter<ApplicationCall> { class OpinionChoiceVoter : Voter() {
enum class Action : ActionI { fun canView(subjects: List<OpinionChoice>, citizen: CitizenI?): VoterResponse =
VIEW canAll(subjects) { canView(it, citizen) }
}
override fun invoke(action: Any, context: ApplicationCall, subject: Any?): VoterResponseI { fun canView(subject: OpinionChoice, citizen: CitizenI?): VoterResponse {
if (!((action is Action) &&
(subject is OpinionChoice?))) return abstain()
if (action == Action.VIEW) {
if (subject is OpinionChoice) {
return granted() return granted()
} }
throw NoSubjectDefinedException(action)
}
return abstain()
}
} }

View File

@@ -3,19 +3,12 @@ package unit.voter
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.auth.User import fr.dcproject.component.auth.User
import fr.dcproject.component.auth.UserI import fr.dcproject.component.auth.UserI
import fr.dcproject.component.auth.user
import fr.dcproject.component.citizen.CitizenBasic import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenCart import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.OpinionChoice import fr.dcproject.entity.OpinionChoice
import fr.dcproject.security.voter.OpinionChoiceVoter import fr.dcproject.security.voter.OpinionChoiceVoter
import fr.ktorVoter.ActionI import fr.dcproject.voter.Vote.GRANTED
import fr.ktorVoter.Vote
import fr.ktorVoter.can
import fr.ktorVoter.canAll
import io.ktor.application.*
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic import io.mockk.mockkStatic
import org.amshove.kluent.`should be` import org.amshove.kluent.`should be`
import org.joda.time.DateTime import org.joda.time.DateTime
@@ -68,32 +61,16 @@ internal class OpinionChoiceVoterTest {
} }
@Test @Test
fun `support opinion choice`(): Unit = OpinionChoiceVoter().run { fun `can be view the opinion choice`() {
val p = object : ActionI {} OpinionChoiceVoter()
mockk<ApplicationCall> { .canView(choice1, tesla)
every { user } returns tesla.user .vote `should be` GRANTED
}.let {
this(OpinionChoiceVoter.Action.VIEW, it, choice1).vote `should be` Vote.GRANTED
this(OpinionChoiceVoter.Action.VIEW, it, article1).vote `should be` Vote.ABSTAIN
this(p, it, choice1).vote `should be` Vote.ABSTAIN
}
} }
@Test @Test
fun `can be view the opinion choice`(): Unit = listOf(OpinionChoiceVoter()).run { fun `can be view the opinion choice list`() {
mockk<ApplicationCall> { OpinionChoiceVoter()
every { user } returns tesla.user .canView(listOf(choice1), tesla)
}.let { .vote `should be` GRANTED
can(OpinionChoiceVoter.Action.VIEW, it, choice1) `should be` true
}
}
@Test
fun `can be view the opinion choice list`(): Unit = listOf(OpinionChoiceVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
canAll(OpinionChoiceVoter.Action.VIEW, it, listOf(choice1)) `should be` true
}
} }
} }