Refactoring of ConstitutionVoter
This commit is contained in:
@@ -96,7 +96,6 @@ fun Application.module(env: Env = PROD) {
|
|||||||
|
|
||||||
install(AuthorizationVoter) {
|
install(AuthorizationVoter) {
|
||||||
voters = listOf(
|
voters = listOf(
|
||||||
ConstitutionVoter(),
|
|
||||||
VoteVoter(),
|
VoteVoter(),
|
||||||
FollowVoter(),
|
FollowVoter(),
|
||||||
OpinionVoter(),
|
OpinionVoter(),
|
||||||
@@ -208,7 +207,7 @@ fun Application.module(env: Env = PROD) {
|
|||||||
deleteMemberOfWorkgroup(get(), get())
|
deleteMemberOfWorkgroup(get(), get())
|
||||||
updateMemberOfWorkgroup(get(), get())
|
updateMemberOfWorkgroup(get(), get())
|
||||||
/* TODO */
|
/* TODO */
|
||||||
constitution(get())
|
constitution(get(), get())
|
||||||
followArticle(get())
|
followArticle(get())
|
||||||
followConstitution(get())
|
followConstitution(get())
|
||||||
commentConstitution(get(), get())
|
commentConstitution(get(), get())
|
||||||
|
|||||||
@@ -23,6 +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.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
|
||||||
@@ -123,6 +124,7 @@ val KoinModule = module {
|
|||||||
single { CitizenVoter() }
|
single { CitizenVoter() }
|
||||||
single { CommentVoter() }
|
single { CommentVoter() }
|
||||||
single { WorkgroupVoter() }
|
single { WorkgroupVoter() }
|
||||||
|
single { ConstitutionVoter() }
|
||||||
|
|
||||||
// Elasticsearch Client
|
// Elasticsearch Client
|
||||||
single<RestClient> {
|
single<RestClient> {
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ package fr.dcproject.routes
|
|||||||
|
|
||||||
import fr.dcproject.component.article.ArticleRef
|
import fr.dcproject.component.article.ArticleRef
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.CitizenWithUserI
|
import fr.dcproject.component.citizen.CitizenWithUserI
|
||||||
import fr.dcproject.entity.ConstitutionSimple
|
import fr.dcproject.entity.ConstitutionSimple
|
||||||
import fr.dcproject.entity.ConstitutionSimple.TitleSimple
|
import fr.dcproject.entity.ConstitutionSimple.TitleSimple
|
||||||
import fr.dcproject.security.voter.ConstitutionVoter.Action.CREATE
|
import fr.dcproject.security.voter.ConstitutionVoter
|
||||||
import fr.dcproject.security.voter.ConstitutionVoter.Action.VIEW
|
import fr.dcproject.voter.assert
|
||||||
import fr.ktorVoter.assertCan
|
|
||||||
import fr.ktorVoter.assertCanAll
|
|
||||||
import fr.postgresjson.entity.UuidEntity
|
import fr.postgresjson.entity.UuidEntity
|
||||||
import fr.postgresjson.repository.RepositoryI
|
import fr.postgresjson.repository.RepositoryI
|
||||||
import io.ktor.application.*
|
import io.ktor.application.*
|
||||||
@@ -82,21 +81,21 @@ object ConstitutionPaths {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@KtorExperimentalLocationsAPI
|
@KtorExperimentalLocationsAPI
|
||||||
fun Route.constitution(repo: ConstitutionRepository) {
|
fun Route.constitution(repo: ConstitutionRepository, voter: ConstitutionVoter) {
|
||||||
get<ConstitutionPaths.ConstitutionsRequest> {
|
get<ConstitutionPaths.ConstitutionsRequest> {
|
||||||
val constitutions = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
|
val constitutions = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
|
||||||
assertCanAll(VIEW, constitutions.result)
|
voter.assert { canView(constitutions.result, citizenOrNull) }
|
||||||
call.respond(constitutions)
|
call.respond(constitutions)
|
||||||
}
|
}
|
||||||
|
|
||||||
get<ConstitutionPaths.ConstitutionRequest> {
|
get<ConstitutionPaths.ConstitutionRequest> {
|
||||||
assertCan(VIEW, it.constitution)
|
voter.assert { canView(it.constitution, citizenOrNull) }
|
||||||
call.respond(it.constitution)
|
call.respond(it.constitution)
|
||||||
}
|
}
|
||||||
|
|
||||||
post<ConstitutionPaths.PostConstitutionRequest> {
|
post<ConstitutionPaths.PostConstitutionRequest> {
|
||||||
it.getNewConstitution(call).let { constitution ->
|
it.getNewConstitution(call).let { constitution ->
|
||||||
assertCan(CREATE, constitution)
|
voter.assert { canCreate(constitution, citizenOrNull) }
|
||||||
repo.upsert(constitution)
|
repo.upsert(constitution)
|
||||||
call.respond(constitution)
|
call.respond(constitution)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,67 +1,34 @@
|
|||||||
package fr.dcproject.security.voter
|
package fr.dcproject.security.voter
|
||||||
|
|
||||||
import fr.dcproject.component.auth.UserI
|
import fr.dcproject.component.citizen.CitizenI
|
||||||
import fr.dcproject.component.auth.user
|
import fr.dcproject.entity.ConstitutionS
|
||||||
import fr.dcproject.component.comment.generic.CommentForView
|
|
||||||
import fr.dcproject.entity.ConstitutionSimple
|
import fr.dcproject.entity.ConstitutionSimple
|
||||||
import fr.dcproject.voter.NoRuleDefinedException
|
import fr.dcproject.voter.Voter
|
||||||
import fr.dcproject.voter.NoSubjectDefinedException
|
import fr.dcproject.voter.VoterResponse
|
||||||
import fr.ktorVoter.*
|
|
||||||
import io.ktor.application.*
|
|
||||||
import fr.dcproject.entity.Vote as VoteEntity
|
|
||||||
|
|
||||||
class ConstitutionVoter : Voter<ApplicationCall> {
|
class ConstitutionVoter : Voter() {
|
||||||
enum class Action : ActionI {
|
fun canCreate(subject: ConstitutionS, citizen: CitizenI?): VoterResponse = when {
|
||||||
CREATE,
|
citizen == null -> denied("You must be connected to create constitution", "constitution.create.notConnected")
|
||||||
UPDATE,
|
else -> granted()
|
||||||
VIEW,
|
|
||||||
DELETE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invoke(action: Any, context: ApplicationCall, subject: Any?): VoterResponseI {
|
fun <S : ConstitutionSimple<*, *>> canView(subjects: List<S>, citizen: CitizenI?): VoterResponse =
|
||||||
if (!((action is Action || action is VoteVoter.Action) &&
|
canAll(subjects) { canView(it, citizen) }
|
||||||
(subject is ConstitutionSimple<*, *>? || subject is VoteEntity<*> || subject is CommentForView<*, *>))) return abstain()
|
|
||||||
|
|
||||||
val user = context.user
|
fun canView(subject: ConstitutionSimple<*, *>, citizen: CitizenI?): VoterResponse = when {
|
||||||
if (action == Action.CREATE && user != null) {
|
subject.isDeleted() -> denied("You cannot view a deleted constitution", "constitution.view.deleted")
|
||||||
return granted()
|
else -> granted()
|
||||||
}
|
|
||||||
|
|
||||||
if (action == Action.VIEW) {
|
|
||||||
if (subject is ConstitutionSimple<*, *>) {
|
|
||||||
return if (subject.isDeleted()) denied("You cannot view a deleted constitution", "constitution.view.deleted")
|
|
||||||
else granted()
|
|
||||||
}
|
|
||||||
throw NoSubjectDefinedException(action as ActionI)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action == Action.DELETE && user is UserI && subject is ConstitutionSimple<*, *> && subject.createdBy.user.id == user.id) {
|
|
||||||
return granted()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action == Action.UPDATE && user is UserI && subject is ConstitutionSimple<*, *> && subject.createdBy.user.id == user.id) {
|
|
||||||
return granted()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action is VoteVoter.Action) return voteForVote(action, subject)
|
|
||||||
|
|
||||||
if (action is Action) {
|
|
||||||
throw NoRuleDefinedException(action)
|
|
||||||
}
|
|
||||||
|
|
||||||
return abstain()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun voteForVote(action: VoteVoter.Action, subject: Any?): VoterResponseI {
|
fun canDelete(subject: ConstitutionSimple<*, *>, citizen: CitizenI?): VoterResponse = when {
|
||||||
if (action == VoteVoter.Action.CREATE && subject is VoteEntity<*>) {
|
citizen == null -> denied("You must be connected to delete constitution", "constitution.delete.notConnected")
|
||||||
val target = subject.target
|
subject.createdBy.id != citizen.id -> denied("You cannot delete the constitution of other citizen", "constitution.delete.otherCitizen")
|
||||||
if (target !is ConstitutionSimple<*, *>) {
|
else -> granted()
|
||||||
return abstain()
|
}
|
||||||
}
|
|
||||||
if (target.isDeleted()) {
|
fun canUpdate(subject: ConstitutionSimple<*, *>, citizen: CitizenI?): VoterResponse = when {
|
||||||
return denied("You cannot vote a deleted constitution", "constitution.vote.deleted")
|
citizen == null -> denied("You must be connected to update constitution", "constitution.update.notConnected")
|
||||||
}
|
subject.createdBy.id != citizen.id -> denied("You cannot update the constitution of other citizen", "constitution.update.otherCitizen")
|
||||||
}
|
else -> granted()
|
||||||
return abstain()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user