Big refactoring #77

Merged
flecomte merged 166 commits from refactoring-component-and-immutable into master 2021-03-24 19:06:07 +01:00
4 changed files with 33 additions and 66 deletions
Showing only changes of commit 308a284280 - Show all commits

View File

@@ -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())

View File

@@ -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> {

View File

@@ -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)
} }

View File

@@ -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()
} }
} }