Fix security
This commit is contained in:
@@ -36,9 +36,11 @@ class ArticleForUpdate(
|
||||
tags: List<String> = emptyList(),
|
||||
val draft: Boolean = false,
|
||||
val createdBy: CitizenRef,
|
||||
val workgroup: WorkgroupRef? = null
|
||||
) : ArticleRefVersioning(id) {
|
||||
val workgroup: WorkgroupRef? = null,
|
||||
versionId: UUID?
|
||||
) : ArticleRefVersioning(id, versionId = versionId ?: UUID.randomUUID()) {
|
||||
val tags: List<String> = tags.distinct()
|
||||
val isNew = versionId == null
|
||||
}
|
||||
|
||||
open class ArticleSimple(
|
||||
|
||||
@@ -11,6 +11,7 @@ import fr.dcproject.event.raiseEvent
|
||||
import fr.dcproject.repository.Article.Filter
|
||||
import fr.dcproject.repository.Workgroup as WorkgroupRepository
|
||||
import fr.dcproject.security.voter.ArticleVoter.Action.CREATE
|
||||
import fr.dcproject.security.voter.ArticleVoter.Action.UPDATE
|
||||
import fr.dcproject.security.voter.ArticleVoter.Action.VIEW
|
||||
import fr.dcproject.views.ArticleViewManager
|
||||
import fr.ktorVoter.assertCan
|
||||
@@ -81,18 +82,17 @@ object ArticlesPaths {
|
||||
|
||||
suspend fun getNewArticle(call: ApplicationCall): ArticleForUpdate = call.receive<Article>().run {
|
||||
ArticleForUpdate(
|
||||
id ?: UUID.randomUUID(),
|
||||
title,
|
||||
anonymous,
|
||||
content,
|
||||
description,
|
||||
tags,
|
||||
draft,
|
||||
id = id ?: UUID.randomUUID(),
|
||||
title = title,
|
||||
anonymous = anonymous,
|
||||
content = content,
|
||||
description = description,
|
||||
tags = tags,
|
||||
draft = draft,
|
||||
createdBy = call.citizen,
|
||||
workgroup = if (workgroup != null) workgroupRepository.findById(workgroup.id) as WorkgroupSimple<CitizenRef> else null
|
||||
).also {
|
||||
it.versionId = versionId ?: UUID.randomUUID()
|
||||
}
|
||||
workgroup = if (workgroup != null) workgroupRepository.findById(workgroup.id) as WorkgroupSimple<CitizenRef> else null,
|
||||
versionId = versionId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,11 @@ fun Route.article(repo: ArticleRepository, viewManager: ArticleViewManager) {
|
||||
|
||||
post<ArticlesPaths.PostArticleRequest> {
|
||||
it.getNewArticle(call).also { article ->
|
||||
assertCan(CREATE, article)
|
||||
if(article.isNew) {
|
||||
assertCan(CREATE, article)
|
||||
} else {
|
||||
assertCan(UPDATE, article)
|
||||
}
|
||||
val newArticle = repo.upsert(article) ?: error("Article not updated")
|
||||
call.respond(article)
|
||||
raiseEvent(ArticleUpdate.event, ArticleUpdate(newArticle))
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
package fr.dcproject.security.voter
|
||||
|
||||
import fr.dcproject.citizenOrNull
|
||||
import fr.dcproject.entity.ArticleAuthI
|
||||
import fr.dcproject.entity.ArticleForUpdate
|
||||
import fr.dcproject.entity.ArticleI
|
||||
import fr.dcproject.entity.Citizen as CitizenEntity
|
||||
import fr.dcproject.entity.CitizenI
|
||||
import fr.dcproject.entity.UserI
|
||||
import fr.dcproject.repository.Article as ArticleRepo
|
||||
import fr.dcproject.user
|
||||
import fr.ktorVoter.ActionI
|
||||
import fr.ktorVoter.Vote
|
||||
import fr.ktorVoter.Voter
|
||||
import fr.ktorVoter.checkClass
|
||||
import io.ktor.application.ApplicationCall
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.inject
|
||||
import fr.dcproject.entity.Comment as CommentEntity
|
||||
import fr.dcproject.entity.Vote as VoteEntity
|
||||
|
||||
class ArticleVoter : Voter {
|
||||
class ArticleVoter : Voter, KoinComponent {
|
||||
private val articleRepo: ArticleRepo by inject()
|
||||
enum class Action : ActionI {
|
||||
CREATE,
|
||||
UPDATE,
|
||||
@@ -30,7 +38,7 @@ class ArticleVoter : Voter {
|
||||
if (action == Action.CREATE && user is UserI) return Vote.GRANTED
|
||||
if (action == Action.VIEW) return view(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, call.citizenOrNull)
|
||||
if (action is CommentVoter.Action) return voteForComment(action, subject)
|
||||
if (action is VoteVoter.Action) return voteForVote(action, subject)
|
||||
if (action is Action) return Vote.DENIED
|
||||
@@ -58,11 +66,16 @@ class ArticleVoter : Voter {
|
||||
return Vote.DENIED
|
||||
}
|
||||
|
||||
private fun update(subject: Any?, user: UserI?): Vote {
|
||||
checkClass(ArticleAuthI::class, subject)
|
||||
if (subject is ArticleAuthI<*>) {
|
||||
if (user is UserI && subject.createdBy.user.id == user.id) {
|
||||
return Vote.GRANTED
|
||||
private fun update(subject: Any?, citizen: CitizenEntity?): Vote {
|
||||
checkClass(ArticleForUpdate::class, subject)
|
||||
if (subject is ArticleForUpdate) {
|
||||
/* The new Article must by created by the same citizen of the connected citizen */
|
||||
if (citizen is CitizenI && subject.createdBy.id == citizen.id) {
|
||||
/* The creator must be the same of the creator of preview version of article */
|
||||
if(articleRepo.findVerionsByVersionsId(1, 1, subject.versionId).result.first().createdBy.id == citizen.id) {
|
||||
return Vote.GRANTED
|
||||
}
|
||||
return Vote.DENIED
|
||||
}
|
||||
}
|
||||
return Vote.DENIED
|
||||
|
||||
@@ -73,7 +73,8 @@ class ArticleSteps : En, KoinTest {
|
||||
content = "bla bla bla",
|
||||
description = "A super article",
|
||||
createdBy = createdBy,
|
||||
workgroup = workgroup
|
||||
workgroup = workgroup,
|
||||
versionId = UUID.randomUUID()
|
||||
)
|
||||
get<ArticleRepository>().upsert(article)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user