diff --git a/src/main/kotlin/application/Application.kt b/src/main/kotlin/application/Application.kt index 3c3050f..f297790 100644 --- a/src/main/kotlin/application/Application.kt +++ b/src/main/kotlin/application/Application.kt @@ -96,7 +96,6 @@ fun Application.module(env: Env = PROD) { install(AuthorizationVoter) { voters = listOf( - ConstitutionVoter(), VoteVoter(), FollowVoter(), OpinionVoter(), @@ -208,7 +207,7 @@ fun Application.module(env: Env = PROD) { deleteMemberOfWorkgroup(get(), get()) updateMemberOfWorkgroup(get(), get()) /* TODO */ - constitution(get()) + constitution(get(), get()) followArticle(get()) followConstitution(get()) commentConstitution(get(), get()) diff --git a/src/main/kotlin/application/KoinModule.kt b/src/main/kotlin/application/KoinModule.kt index 2072372..495d867 100644 --- a/src/main/kotlin/application/KoinModule.kt +++ b/src/main/kotlin/application/KoinModule.kt @@ -23,6 +23,7 @@ import fr.dcproject.event.publisher.Publisher import fr.dcproject.messages.Mailer import fr.dcproject.messages.NotificationEmailSender import fr.dcproject.repository.CommentConstitutionRepository +import fr.dcproject.security.voter.ConstitutionVoter import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Requester import fr.postgresjson.migration.Migrations @@ -123,6 +124,7 @@ val KoinModule = module { single { CitizenVoter() } single { CommentVoter() } single { WorkgroupVoter() } + single { ConstitutionVoter() } // Elasticsearch Client single { diff --git a/src/main/kotlin/routes/Constitution.kt b/src/main/kotlin/routes/Constitution.kt index 3d75098..2055822 100644 --- a/src/main/kotlin/routes/Constitution.kt +++ b/src/main/kotlin/routes/Constitution.kt @@ -2,13 +2,12 @@ package fr.dcproject.routes import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.auth.citizen +import fr.dcproject.component.auth.citizenOrNull import fr.dcproject.component.citizen.CitizenWithUserI import fr.dcproject.entity.ConstitutionSimple import fr.dcproject.entity.ConstitutionSimple.TitleSimple -import fr.dcproject.security.voter.ConstitutionVoter.Action.CREATE -import fr.dcproject.security.voter.ConstitutionVoter.Action.VIEW -import fr.ktorVoter.assertCan -import fr.ktorVoter.assertCanAll +import fr.dcproject.security.voter.ConstitutionVoter +import fr.dcproject.voter.assert import fr.postgresjson.entity.UuidEntity import fr.postgresjson.repository.RepositoryI import io.ktor.application.* @@ -82,21 +81,21 @@ object ConstitutionPaths { } @KtorExperimentalLocationsAPI -fun Route.constitution(repo: ConstitutionRepository) { +fun Route.constitution(repo: ConstitutionRepository, voter: ConstitutionVoter) { get { 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) } get { - assertCan(VIEW, it.constitution) + voter.assert { canView(it.constitution, citizenOrNull) } call.respond(it.constitution) } post { it.getNewConstitution(call).let { constitution -> - assertCan(CREATE, constitution) + voter.assert { canCreate(constitution, citizenOrNull) } repo.upsert(constitution) call.respond(constitution) } diff --git a/src/main/kotlin/voter/ConstitutionVoter.kt b/src/main/kotlin/voter/ConstitutionVoter.kt index f24a644..1133ada 100644 --- a/src/main/kotlin/voter/ConstitutionVoter.kt +++ b/src/main/kotlin/voter/ConstitutionVoter.kt @@ -1,67 +1,34 @@ package fr.dcproject.security.voter -import fr.dcproject.component.auth.UserI -import fr.dcproject.component.auth.user -import fr.dcproject.component.comment.generic.CommentForView +import fr.dcproject.component.citizen.CitizenI +import fr.dcproject.entity.ConstitutionS import fr.dcproject.entity.ConstitutionSimple -import fr.dcproject.voter.NoRuleDefinedException -import fr.dcproject.voter.NoSubjectDefinedException -import fr.ktorVoter.* -import io.ktor.application.* -import fr.dcproject.entity.Vote as VoteEntity +import fr.dcproject.voter.Voter +import fr.dcproject.voter.VoterResponse -class ConstitutionVoter : Voter { - enum class Action : ActionI { - CREATE, - UPDATE, - VIEW, - DELETE +class ConstitutionVoter : Voter() { + fun canCreate(subject: ConstitutionS, citizen: CitizenI?): VoterResponse = when { + citizen == null -> denied("You must be connected to create constitution", "constitution.create.notConnected") + else -> granted() } - override fun invoke(action: Any, context: ApplicationCall, subject: Any?): VoterResponseI { - if (!((action is Action || action is VoteVoter.Action) && - (subject is ConstitutionSimple<*, *>? || subject is VoteEntity<*> || subject is CommentForView<*, *>))) return abstain() + fun > canView(subjects: List, citizen: CitizenI?): VoterResponse = + canAll(subjects) { canView(it, citizen) } - val user = context.user - if (action == Action.CREATE && user != null) { - return 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() + fun canView(subject: ConstitutionSimple<*, *>, citizen: CitizenI?): VoterResponse = when { + subject.isDeleted() -> denied("You cannot view a deleted constitution", "constitution.view.deleted") + else -> granted() } - private fun voteForVote(action: VoteVoter.Action, subject: Any?): VoterResponseI { - if (action == VoteVoter.Action.CREATE && subject is VoteEntity<*>) { - val target = subject.target - if (target !is ConstitutionSimple<*, *>) { - return abstain() - } - if (target.isDeleted()) { - return denied("You cannot vote a deleted constitution", "constitution.vote.deleted") - } - } - return abstain() + fun canDelete(subject: ConstitutionSimple<*, *>, citizen: CitizenI?): VoterResponse = when { + citizen == null -> denied("You must be connected to delete constitution", "constitution.delete.notConnected") + subject.createdBy.id != citizen.id -> denied("You cannot delete the constitution of other citizen", "constitution.delete.otherCitizen") + else -> granted() + } + + fun canUpdate(subject: ConstitutionSimple<*, *>, citizen: CitizenI?): VoterResponse = when { + 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() } }