Rename Voter to AccessControl
This commit is contained in:
@@ -28,7 +28,7 @@ import fr.dcproject.routes.commentConstitution
|
|||||||
import fr.dcproject.routes.constitution
|
import fr.dcproject.routes.constitution
|
||||||
import fr.dcproject.routes.definition
|
import fr.dcproject.routes.definition
|
||||||
import fr.dcproject.routes.notificationArticle
|
import fr.dcproject.routes.notificationArticle
|
||||||
import fr.dcproject.voter.VoterDeniedException
|
import fr.dcproject.security.AccessDeniedException
|
||||||
import fr.postgresjson.migration.Migrations
|
import fr.postgresjson.migration.Migrations
|
||||||
import io.ktor.application.Application
|
import io.ktor.application.Application
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
@@ -166,7 +166,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<VoterDeniedException> {
|
exception<AccessDeniedException> {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,31 +8,31 @@ import com.fasterxml.jackson.databind.module.SimpleModule
|
|||||||
import com.fasterxml.jackson.datatype.joda.JodaModule
|
import com.fasterxml.jackson.datatype.joda.JodaModule
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import com.rabbitmq.client.ConnectionFactory
|
import com.rabbitmq.client.ConnectionFactory
|
||||||
|
import fr.dcproject.component.article.ArticleAccessControl
|
||||||
import fr.dcproject.component.article.ArticleRepository
|
import fr.dcproject.component.article.ArticleRepository
|
||||||
import fr.dcproject.component.article.ArticleViewManager
|
import fr.dcproject.component.article.ArticleViewManager
|
||||||
import fr.dcproject.component.article.ArticleVoter
|
|
||||||
import fr.dcproject.component.auth.PasswordlessAuth
|
import fr.dcproject.component.auth.PasswordlessAuth
|
||||||
import fr.dcproject.component.auth.UserRepository
|
import fr.dcproject.component.auth.UserRepository
|
||||||
|
import fr.dcproject.component.citizen.CitizenAccessControl
|
||||||
import fr.dcproject.component.citizen.CitizenRepository
|
import fr.dcproject.component.citizen.CitizenRepository
|
||||||
import fr.dcproject.component.citizen.CitizenVoter
|
|
||||||
import fr.dcproject.component.comment.article.CommentArticleRepository
|
import fr.dcproject.component.comment.article.CommentArticleRepository
|
||||||
import fr.dcproject.component.comment.generic.CommentVoter
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
import fr.dcproject.component.follow.FollowVoter
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
|
import fr.dcproject.component.opinion.OpinionAccessControl
|
||||||
|
import fr.dcproject.component.opinion.OpinionChoiceAccessControl
|
||||||
import fr.dcproject.component.opinion.OpinionChoiceRepository
|
import fr.dcproject.component.opinion.OpinionChoiceRepository
|
||||||
import fr.dcproject.component.opinion.OpinionChoiceVoter
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
import fr.dcproject.component.opinion.OpinionVoter
|
|
||||||
import fr.dcproject.component.vote.VoteArticleRepository
|
import fr.dcproject.component.vote.VoteArticleRepository
|
||||||
import fr.dcproject.component.vote.VoteCommentRepository
|
import fr.dcproject.component.vote.VoteCommentRepository
|
||||||
import fr.dcproject.component.vote.VoteConstitutionRepository
|
import fr.dcproject.component.vote.VoteConstitutionRepository
|
||||||
import fr.dcproject.component.vote.VoteRepository
|
import fr.dcproject.component.vote.VoteRepository
|
||||||
import fr.dcproject.component.vote.VoteVoter
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.WorkgroupRepository
|
import fr.dcproject.component.workgroup.WorkgroupRepository
|
||||||
import fr.dcproject.component.workgroup.WorkgroupVoter
|
|
||||||
import fr.dcproject.event.publisher.Publisher
|
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.ConstitutionAccessControl
|
||||||
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
|
||||||
@@ -125,16 +125,16 @@ val KoinModule = module {
|
|||||||
single { OpinionArticleRepository(get()) }
|
single { OpinionArticleRepository(get()) }
|
||||||
single { WorkgroupRepository(get()) }
|
single { WorkgroupRepository(get()) }
|
||||||
|
|
||||||
// Voters
|
// AccessControl
|
||||||
single { ArticleVoter(get()) }
|
single { ArticleAccessControl(get()) }
|
||||||
single { CitizenVoter() }
|
single { CitizenAccessControl() }
|
||||||
single { CommentVoter() }
|
single { CommentAccessControl() }
|
||||||
single { WorkgroupVoter() }
|
single { WorkgroupAccessControl() }
|
||||||
single { ConstitutionVoter() }
|
single { ConstitutionAccessControl() }
|
||||||
single { VoteVoter() }
|
single { VoteAccessControl() }
|
||||||
single { FollowVoter() }
|
single { FollowAccessControl() }
|
||||||
single { OpinionVoter() }
|
single { OpinionAccessControl() }
|
||||||
single { OpinionChoiceVoter() }
|
single { OpinionChoiceAccessControl() }
|
||||||
|
|
||||||
// Elasticsearch Client
|
// Elasticsearch Client
|
||||||
single<RestClient> {
|
single<RestClient> {
|
||||||
|
|||||||
@@ -3,20 +3,20 @@ package fr.dcproject.component.article
|
|||||||
import fr.dcproject.component.citizen.CitizenI
|
import fr.dcproject.component.citizen.CitizenI
|
||||||
import fr.dcproject.entity.CreatedBy
|
import fr.dcproject.entity.CreatedBy
|
||||||
import fr.dcproject.entity.VersionableRef
|
import fr.dcproject.entity.VersionableRef
|
||||||
import fr.dcproject.voter.Voter
|
import fr.dcproject.security.AccessControl
|
||||||
import fr.dcproject.voter.VoterResponse
|
import fr.dcproject.security.AccessResponse
|
||||||
|
|
||||||
class ArticleVoter(private val articleRepo: ArticleRepository) : Voter() {
|
class ArticleAccessControl(private val articleRepo: ArticleRepository) : AccessControl() {
|
||||||
fun <S : ArticleAuthI<*>> canView(subjects: List<S>, citizen: CitizenI?): VoterResponse =
|
fun <S : ArticleAuthI<*>> canView(subjects: List<S>, citizen: CitizenI?): AccessResponse =
|
||||||
canAll(subjects) { canView(it, citizen) }
|
canAll(subjects) { canView(it, citizen) }
|
||||||
|
|
||||||
fun <S : ArticleAuthI<*>> canView(subject: S, citizen: CitizenI?): VoterResponse {
|
fun <S : ArticleAuthI<*>> canView(subject: S, citizen: CitizenI?): AccessResponse {
|
||||||
return if (subject.isDeleted()) denied("Article is deleted", "article.deleted")
|
return if (subject.isDeleted()) denied("Article is deleted", "article.deleted")
|
||||||
else if (subject.draft && (citizen == null || subject.createdBy.id != citizen.id)) denied("Article is draft, but it's not yours", "article.draft.not.yours")
|
else if (subject.draft && (citizen == null || subject.createdBy.id != citizen.id)) denied("Article is draft, but it's not yours", "article.draft.not.yours")
|
||||||
else granted()
|
else granted()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <S : CreatedBy<*>> canDelete(subject: S, citizen: CitizenI?): VoterResponse {
|
fun <S : CreatedBy<*>> canDelete(subject: S, citizen: CitizenI?): AccessResponse {
|
||||||
if (citizen == null) return denied("You must be connected to create article", "article.create.notConnected")
|
if (citizen == null) return denied("You must be connected to create article", "article.create.notConnected")
|
||||||
return if (subject.createdBy.id == citizen.id) {
|
return if (subject.createdBy.id == citizen.id) {
|
||||||
granted()
|
granted()
|
||||||
@@ -25,7 +25,7 @@ class ArticleVoter(private val articleRepo: ArticleRepository) : Voter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <S> canUpsert(subject: S, citizen: CitizenI?): VoterResponse
|
fun <S> canUpsert(subject: S, citizen: CitizenI?): AccessResponse
|
||||||
where S : ArticleI,
|
where S : ArticleI,
|
||||||
S : CreatedBy<*>,
|
S : CreatedBy<*>,
|
||||||
S : VersionableRef {
|
S : VersionableRef {
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package fr.dcproject.component.article.routes
|
package fr.dcproject.component.article.routes
|
||||||
|
|
||||||
|
import fr.dcproject.component.article.ArticleAccessControl
|
||||||
import fr.dcproject.component.article.ArticleForView
|
import fr.dcproject.component.article.ArticleForView
|
||||||
import fr.dcproject.component.article.ArticleRepository
|
import fr.dcproject.component.article.ArticleRepository
|
||||||
import fr.dcproject.component.article.ArticleVoter
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import fr.postgresjson.repository.RepositoryI
|
import fr.postgresjson.repository.RepositoryI
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -31,10 +31,10 @@ object FindArticleVersions {
|
|||||||
private fun ArticleRepository.findVersions(request: ArticleVersionsRequest) =
|
private fun ArticleRepository.findVersions(request: ArticleVersionsRequest) =
|
||||||
findVersionsByVersionId(request.page, request.limit, request.article.versionId)
|
findVersionsByVersionId(request.page, request.limit, request.article.versionId)
|
||||||
|
|
||||||
fun Route.findArticleVersions(repo: ArticleRepository, voter: ArticleVoter) {
|
fun Route.findArticleVersions(repo: ArticleRepository, ac: ArticleAccessControl) {
|
||||||
get<ArticleVersionsRequest> {
|
get<ArticleVersionsRequest> {
|
||||||
repo.findVersions(it)
|
repo.findVersions(it)
|
||||||
.apply { voter.assert { canView(it.article, citizenOrNull) } }
|
.apply { ac.assert { canView(it.article, citizenOrNull) } }
|
||||||
.let { call.respond(it) }
|
.let { call.respond(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package fr.dcproject.component.article.routes
|
package fr.dcproject.component.article.routes
|
||||||
|
|
||||||
|
import fr.dcproject.component.article.ArticleAccessControl
|
||||||
import fr.dcproject.component.article.ArticleForListing
|
import fr.dcproject.component.article.ArticleForListing
|
||||||
import fr.dcproject.component.article.ArticleRepository
|
import fr.dcproject.component.article.ArticleRepository
|
||||||
import fr.dcproject.component.article.ArticleVoter
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.routes.PaginatedRequest
|
import fr.dcproject.routes.PaginatedRequest
|
||||||
import fr.dcproject.routes.PaginatedRequestI
|
import fr.dcproject.routes.PaginatedRequestI
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import fr.postgresjson.connexion.Paginated
|
import fr.postgresjson.connexion.Paginated
|
||||||
import fr.postgresjson.repository.RepositoryI
|
import fr.postgresjson.repository.RepositoryI
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
@@ -40,10 +40,10 @@ object FindArticles {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.findArticles(repo: ArticleRepository, voter: ArticleVoter) {
|
fun Route.findArticles(repo: ArticleRepository, ac: ArticleAccessControl) {
|
||||||
get<ArticlesRequest> {
|
get<ArticlesRequest> {
|
||||||
repo.findArticles(it)
|
repo.findArticles(it)
|
||||||
.apply { voter.assert { canView(result, citizenOrNull) } }
|
.apply { ac.assert { canView(result, citizenOrNull) } }
|
||||||
.let { call.respond(it) }
|
.let { call.respond(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package fr.dcproject.component.article.routes
|
package fr.dcproject.component.article.routes
|
||||||
|
|
||||||
|
import fr.dcproject.component.article.ArticleAccessControl
|
||||||
import fr.dcproject.component.article.ArticleForView
|
import fr.dcproject.component.article.ArticleForView
|
||||||
import fr.dcproject.component.article.ArticleRepository
|
import fr.dcproject.component.article.ArticleRepository
|
||||||
import fr.dcproject.component.article.ArticleViewManager
|
import fr.dcproject.component.article.ArticleViewManager
|
||||||
import fr.dcproject.component.article.ArticleVoter
|
|
||||||
import fr.dcproject.component.article.routes.GetOneArticle.ArticleRequest.Output
|
import fr.dcproject.component.article.routes.GetOneArticle.ArticleRequest.Output
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.opinion.dto.Opinionable
|
import fr.dcproject.component.opinion.dto.Opinionable
|
||||||
@@ -11,7 +11,7 @@ import fr.dcproject.component.vote.dto.Votable
|
|||||||
import fr.dcproject.dto.CreatedAt
|
import fr.dcproject.dto.CreatedAt
|
||||||
import fr.dcproject.dto.Versionable
|
import fr.dcproject.dto.Versionable
|
||||||
import fr.dcproject.dto.Viewable
|
import fr.dcproject.dto.Viewable
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.features.NotFoundException
|
import io.ktor.features.NotFoundException
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -53,9 +53,9 @@ object GetOneArticle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.getOneArticle(viewManager: ArticleViewManager, voter: ArticleVoter) {
|
fun Route.getOneArticle(viewManager: ArticleViewManager, ac: ArticleAccessControl) {
|
||||||
get<ArticleRequest> {
|
get<ArticleRequest> {
|
||||||
voter.assert { canView(it.article, citizenOrNull) }
|
ac.assert { canView(it.article, citizenOrNull) }
|
||||||
|
|
||||||
Output(
|
Output(
|
||||||
it.article,
|
it.article,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package fr.dcproject.component.article.routes
|
package fr.dcproject.component.article.routes
|
||||||
|
|
||||||
|
import fr.dcproject.component.article.ArticleAccessControl
|
||||||
import fr.dcproject.component.article.ArticleForUpdate
|
import fr.dcproject.component.article.ArticleForUpdate
|
||||||
import fr.dcproject.component.article.ArticleForView
|
import fr.dcproject.component.article.ArticleForView
|
||||||
import fr.dcproject.component.article.ArticleRepository
|
import fr.dcproject.component.article.ArticleRepository
|
||||||
import fr.dcproject.component.article.ArticleVoter
|
|
||||||
import fr.dcproject.component.article.routes.UpsertArticle.UpsertArticleRequest.Input
|
import fr.dcproject.component.article.routes.UpsertArticle.UpsertArticleRequest.Input
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
@@ -11,7 +11,7 @@ import fr.dcproject.component.workgroup.WorkgroupRef
|
|||||||
import fr.dcproject.component.workgroup.WorkgroupRepository
|
import fr.dcproject.component.workgroup.WorkgroupRepository
|
||||||
import fr.dcproject.event.ArticleUpdate
|
import fr.dcproject.event.ArticleUpdate
|
||||||
import fr.dcproject.event.raiseEvent
|
import fr.dcproject.event.raiseEvent
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.ApplicationCall
|
import io.ktor.application.ApplicationCall
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -39,7 +39,7 @@ object UpsertArticle {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.upsertArticle(repo: ArticleRepository, workgroupRepository: WorkgroupRepository, voter: ArticleVoter) {
|
fun Route.upsertArticle(repo: ArticleRepository, workgroupRepository: WorkgroupRepository, ac: ArticleAccessControl) {
|
||||||
suspend fun ApplicationCall.convertRequestToEntity(): ArticleForUpdate = receive<Input>().run {
|
suspend fun ApplicationCall.convertRequestToEntity(): ArticleForUpdate = receive<Input>().run {
|
||||||
ArticleForUpdate(
|
ArticleForUpdate(
|
||||||
id = id ?: UUID.randomUUID(),
|
id = id ?: UUID.randomUUID(),
|
||||||
@@ -57,7 +57,7 @@ object UpsertArticle {
|
|||||||
|
|
||||||
post<UpsertArticleRequest> {
|
post<UpsertArticleRequest> {
|
||||||
val article = call.convertRequestToEntity()
|
val article = call.convertRequestToEntity()
|
||||||
voter.assert { canUpsert(article, citizenOrNull) }
|
ac.assert { canUpsert(article, citizenOrNull) }
|
||||||
val newArticle: ArticleForView = repo.upsert(article) ?: error("Article not updated")
|
val newArticle: ArticleForView = repo.upsert(article) ?: error("Article not updated")
|
||||||
call.respond(newArticle)
|
call.respond(newArticle)
|
||||||
raiseEvent(ArticleUpdate.event, ArticleUpdate(newArticle))
|
raiseEvent(ArticleUpdate.event, ArticleUpdate(newArticle))
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
package fr.dcproject.component.citizen
|
package fr.dcproject.component.citizen
|
||||||
|
|
||||||
import fr.dcproject.voter.Voter
|
import fr.dcproject.security.AccessControl
|
||||||
import fr.dcproject.voter.VoterResponse
|
import fr.dcproject.security.AccessResponse
|
||||||
import fr.postgresjson.entity.EntityDeletedAt
|
import fr.postgresjson.entity.EntityDeletedAt
|
||||||
|
|
||||||
class CitizenVoter : Voter() {
|
class CitizenAccessControl : AccessControl() {
|
||||||
fun <S> canView(subjects: List<S>, connectedCitizen: CitizenI?): VoterResponse where S : CitizenI, S : EntityDeletedAt =
|
fun <S> canView(subjects: List<S>, connectedCitizen: CitizenI?): AccessResponse where S : CitizenI, S : EntityDeletedAt =
|
||||||
canAll(subjects) { canView(it, connectedCitizen) }
|
canAll(subjects) { canView(it, connectedCitizen) }
|
||||||
|
|
||||||
fun <S> canView(subject: S, connectedCitizen: CitizenI?): VoterResponse where S : CitizenI, S : EntityDeletedAt {
|
fun <S> canView(subject: S, connectedCitizen: CitizenI?): AccessResponse where S : CitizenI, S : EntityDeletedAt {
|
||||||
if (connectedCitizen == null) return denied("You must be connected to view citizen", "citizen.view.connected")
|
if (connectedCitizen == null) return denied("You must be connected to view citizen", "citizen.view.connected")
|
||||||
return if (subject.isDeleted()) denied("You cannot view a deleted citizen", "citizen.view.deleted")
|
return if (subject.isDeleted()) denied("You cannot view a deleted citizen", "citizen.view.deleted")
|
||||||
else granted()
|
else granted()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <S : CitizenI> canUpdate(subject: S, connectedCitizen: CitizenI?): VoterResponse {
|
fun <S : CitizenI> canUpdate(subject: S, connectedCitizen: CitizenI?): AccessResponse {
|
||||||
if (connectedCitizen == null) return denied("You must be connected to update Citizen", "citizen.update.notConnected")
|
if (connectedCitizen == null) return denied("You must be connected to update Citizen", "citizen.update.notConnected")
|
||||||
return if (subject.id == connectedCitizen.id) granted() else denied("You can only update your citizen", "citizen.update.notYours")
|
return if (subject.id == connectedCitizen.id) granted() else denied("You can only update your citizen", "citizen.update.notYours")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <S : CitizenI> canChangePassword(subject: S, connectedCitizen: CitizenI?): VoterResponse {
|
fun <S : CitizenI> canChangePassword(subject: S, connectedCitizen: CitizenI?): AccessResponse {
|
||||||
if (connectedCitizen == null) return denied("You must be connected to change your password", "citizen.changePassword.notConnected")
|
if (connectedCitizen == null) return denied("You must be connected to change your password", "citizen.changePassword.notConnected")
|
||||||
return if (subject.id == connectedCitizen.id) granted() else denied("You can only change your password", "citizen.password.notYours")
|
return if (subject.id == connectedCitizen.id) granted() else denied("You can only change your password", "citizen.password.notYours")
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,8 @@ import fr.dcproject.component.auth.UserRepository
|
|||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.Citizen
|
import fr.dcproject.component.citizen.Citizen
|
||||||
import fr.dcproject.component.citizen.CitizenVoter
|
import fr.dcproject.component.citizen.CitizenAccessControl
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.auth.UserPasswordCredential
|
import io.ktor.auth.UserPasswordCredential
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
@@ -24,9 +24,9 @@ object ChangeMyPassword {
|
|||||||
data class Input(val oldPassword: String, val newPassword: String)
|
data class Input(val oldPassword: String, val newPassword: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.changeMyPassword(voter: CitizenVoter, userRepository: UserRepository) {
|
fun Route.changeMyPassword(ac: CitizenAccessControl, userRepository: UserRepository) {
|
||||||
put<ChangePasswordCitizenRequest> {
|
put<ChangePasswordCitizenRequest> {
|
||||||
voter.assert { canChangePassword(it.citizen, citizenOrNull) }
|
ac.assert { canChangePassword(it.citizen, citizenOrNull) }
|
||||||
try {
|
try {
|
||||||
val content = call.receive<ChangePasswordCitizenRequest.Input>()
|
val content = call.receive<ChangePasswordCitizenRequest.Input>()
|
||||||
val currentUser = userRepository.findByCredentials(UserPasswordCredential(citizen.user.username, content.oldPassword))
|
val currentUser = userRepository.findByCredentials(UserPasswordCredential(citizen.user.username, content.oldPassword))
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package fr.dcproject.component.citizen.routes
|
package fr.dcproject.component.citizen.routes
|
||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.citizen.CitizenAccessControl
|
||||||
import fr.dcproject.component.citizen.CitizenRepository
|
import fr.dcproject.component.citizen.CitizenRepository
|
||||||
import fr.dcproject.component.citizen.CitizenVoter
|
|
||||||
import fr.dcproject.routes.PaginatedRequest
|
import fr.dcproject.routes.PaginatedRequest
|
||||||
import fr.dcproject.routes.PaginatedRequestI
|
import fr.dcproject.routes.PaginatedRequestI
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import fr.postgresjson.repository.RepositoryI
|
import fr.postgresjson.repository.RepositoryI
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -25,10 +25,10 @@ object FindCitizens {
|
|||||||
val search: String? = null
|
val search: String? = null
|
||||||
) : PaginatedRequestI by PaginatedRequest(page, limit)
|
) : PaginatedRequestI by PaginatedRequest(page, limit)
|
||||||
|
|
||||||
fun Route.findCitizen(voter: CitizenVoter, repo: CitizenRepository) {
|
fun Route.findCitizen(ac: CitizenAccessControl, repo: CitizenRepository) {
|
||||||
get<CitizensRequest> {
|
get<CitizensRequest> {
|
||||||
val citizens = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
|
val citizens = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
|
||||||
voter.assert { canView(citizens.result, citizenOrNull) }
|
ac.assert { canView(citizens.result, citizenOrNull) }
|
||||||
call.respond(citizens)
|
call.respond(citizens)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package fr.dcproject.component.citizen.routes
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.CitizenVoter
|
import fr.dcproject.component.citizen.CitizenAccessControl
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -17,13 +17,13 @@ object GetCurrentCitizen {
|
|||||||
@Location("/citizens/current")
|
@Location("/citizens/current")
|
||||||
class CurrentCitizenRequest
|
class CurrentCitizenRequest
|
||||||
|
|
||||||
fun Route.getCurrentCitizen(voter: CitizenVoter) {
|
fun Route.getCurrentCitizen(ac: CitizenAccessControl) {
|
||||||
get<CurrentCitizenRequest> {
|
get<CurrentCitizenRequest> {
|
||||||
val currentUser = citizenOrNull
|
val currentUser = citizenOrNull
|
||||||
if (currentUser === null) {
|
if (currentUser === null) {
|
||||||
call.respond(HttpStatusCode.Unauthorized)
|
call.respond(HttpStatusCode.Unauthorized)
|
||||||
} else {
|
} else {
|
||||||
voter.assert { canView(currentUser, citizenOrNull) }
|
ac.assert { canView(currentUser, citizenOrNull) }
|
||||||
call.respond(citizen)
|
call.respond(citizen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package fr.dcproject.component.citizen.routes
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.Citizen
|
import fr.dcproject.component.citizen.Citizen
|
||||||
import fr.dcproject.component.citizen.CitizenVoter
|
import fr.dcproject.component.citizen.CitizenAccessControl
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
import io.ktor.locations.Location
|
||||||
@@ -16,9 +16,9 @@ object GetOneCitizen {
|
|||||||
@Location("/citizens/{citizen}")
|
@Location("/citizens/{citizen}")
|
||||||
class CitizenRequest(val citizen: Citizen)
|
class CitizenRequest(val citizen: Citizen)
|
||||||
|
|
||||||
fun Route.getOneCitizen(voter: CitizenVoter) {
|
fun Route.getOneCitizen(ac: CitizenAccessControl) {
|
||||||
get<CitizenRequest> {
|
get<CitizenRequest> {
|
||||||
voter.assert { canView(it.citizen, citizenOrNull) }
|
ac.assert { canView(it.citizen, citizenOrNull) }
|
||||||
|
|
||||||
call.respond(it.citizen)
|
call.respond(it.citizen)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import fr.dcproject.component.article.ArticleForView
|
|||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.comment.article.CommentArticleRepository
|
import fr.dcproject.component.comment.article.CommentArticleRepository
|
||||||
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
import fr.dcproject.component.comment.generic.CommentForUpdate
|
import fr.dcproject.component.comment.generic.CommentForUpdate
|
||||||
import fr.dcproject.component.comment.generic.CommentVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.ApplicationCall
|
import io.ktor.application.ApplicationCall
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
@@ -36,10 +36,10 @@ object CreateCommentArticle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.createCommentArticle(repo: CommentArticleRepository, voter: CommentVoter) {
|
fun Route.createCommentArticle(repo: CommentArticleRepository, ac: CommentAccessControl) {
|
||||||
post<PostArticleCommentRequest> {
|
post<PostArticleCommentRequest> {
|
||||||
it.getComment(call).let { comment ->
|
it.getComment(call).let { comment ->
|
||||||
voter.assert { canCreate(comment, citizenOrNull) }
|
ac.assert { canCreate(comment, citizenOrNull) }
|
||||||
repo.comment(comment)
|
repo.comment(comment)
|
||||||
call.respond(HttpStatusCode.Created, comment)
|
call.respond(HttpStatusCode.Created, comment)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package fr.dcproject.component.comment.article.routes
|
|||||||
import fr.dcproject.component.article.ArticleRef
|
import fr.dcproject.component.article.ArticleRef
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.comment.article.CommentArticleRepository
|
import fr.dcproject.component.comment.article.CommentArticleRepository
|
||||||
import fr.dcproject.component.comment.generic.CommentVoter
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
import fr.dcproject.routes.PaginatedRequest
|
import fr.dcproject.routes.PaginatedRequest
|
||||||
import fr.dcproject.routes.PaginatedRequestI
|
import fr.dcproject.routes.PaginatedRequestI
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -28,11 +28,11 @@ object GetArticleComments {
|
|||||||
val sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.fromString(sort) ?: CommentArticleRepository.Sort.CREATED_AT
|
val sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.fromString(sort) ?: CommentArticleRepository.Sort.CREATED_AT
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.getArticleComments(repo: CommentArticleRepository, voter: CommentVoter) {
|
fun Route.getArticleComments(repo: CommentArticleRepository, ac: CommentAccessControl) {
|
||||||
get<ArticleCommentsRequest> {
|
get<ArticleCommentsRequest> {
|
||||||
val comment = repo.findByTarget(it.article, it.page, it.limit, it.sort)
|
val comment = repo.findByTarget(it.article, it.page, it.limit, it.sort)
|
||||||
if (comment.result.isNotEmpty()) {
|
if (comment.result.isNotEmpty()) {
|
||||||
voter.assert { canView(comment.result, citizenOrNull) }
|
ac.assert { canView(comment.result, citizenOrNull) }
|
||||||
}
|
}
|
||||||
call.respond(HttpStatusCode.OK, comment)
|
call.respond(HttpStatusCode.OK, comment)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package fr.dcproject.component.comment.article.routes
|
|||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.Citizen
|
import fr.dcproject.component.citizen.Citizen
|
||||||
import fr.dcproject.component.comment.article.CommentArticleRepository
|
import fr.dcproject.component.comment.article.CommentArticleRepository
|
||||||
import fr.dcproject.component.comment.generic.CommentVoter
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
import io.ktor.locations.Location
|
||||||
@@ -17,10 +17,10 @@ object GetCitizenArticleComments {
|
|||||||
@Location("/citizens/{citizen}/comments/articles")
|
@Location("/citizens/{citizen}/comments/articles")
|
||||||
class CitizenCommentArticleRequest(val citizen: Citizen)
|
class CitizenCommentArticleRequest(val citizen: Citizen)
|
||||||
|
|
||||||
fun Route.getCitizenArticleComments(repo: CommentArticleRepository, voter: CommentVoter) {
|
fun Route.getCitizenArticleComments(repo: CommentArticleRepository, ac: CommentAccessControl) {
|
||||||
get<CitizenCommentArticleRequest> {
|
get<CitizenCommentArticleRequest> {
|
||||||
repo.findByCitizen(it.citizen).let { comments ->
|
repo.findByCitizen(it.citizen).let { comments ->
|
||||||
voter.assert { canView(comments.result, citizenOrNull) }
|
ac.assert { canView(comments.result, citizenOrNull) }
|
||||||
call.respond(comments)
|
call.respond(comments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,24 +2,24 @@ package fr.dcproject.component.comment.generic
|
|||||||
|
|
||||||
import fr.dcproject.component.citizen.CitizenI
|
import fr.dcproject.component.citizen.CitizenI
|
||||||
import fr.dcproject.entity.HasTarget
|
import fr.dcproject.entity.HasTarget
|
||||||
import fr.dcproject.voter.Voter
|
import fr.dcproject.security.AccessControl
|
||||||
import fr.dcproject.voter.VoterResponse
|
import fr.dcproject.security.AccessResponse
|
||||||
import fr.postgresjson.entity.EntityCreatedBy
|
import fr.postgresjson.entity.EntityCreatedBy
|
||||||
import fr.postgresjson.entity.EntityDeletedAt
|
import fr.postgresjson.entity.EntityDeletedAt
|
||||||
|
|
||||||
class CommentVoter : Voter() {
|
class CommentAccessControl : AccessControl() {
|
||||||
fun <S> canView(subjects: List<S>, citizen: CitizenI?): VoterResponse
|
fun <S> canView(subjects: List<S>, citizen: CitizenI?): AccessResponse
|
||||||
where S : CommentI,
|
where S : CommentI,
|
||||||
S : EntityDeletedAt = canAll(subjects) { canView(it, citizen) }
|
S : EntityDeletedAt = canAll(subjects) { canView(it, citizen) }
|
||||||
|
|
||||||
fun <S> canView(subject: S, citizen: CitizenI?): VoterResponse
|
fun <S> canView(subject: S, citizen: CitizenI?): AccessResponse
|
||||||
where S : CommentI,
|
where S : CommentI,
|
||||||
S : EntityDeletedAt = when {
|
S : EntityDeletedAt = when {
|
||||||
subject.isDeleted() -> denied("Your cannot view a deleted comment", "comment.view.deleted")
|
subject.isDeleted() -> denied("Your cannot view a deleted comment", "comment.view.deleted")
|
||||||
else -> granted()
|
else -> granted()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <S, CR : CitizenI> canCreate(subject: S, citizen: CitizenI?): VoterResponse
|
fun <S, CR : CitizenI> canCreate(subject: S, citizen: CitizenI?): AccessResponse
|
||||||
where S : CommentI,
|
where S : CommentI,
|
||||||
S : EntityCreatedBy<CR>,
|
S : EntityCreatedBy<CR>,
|
||||||
S : CommentWithParentI<*>,
|
S : CommentWithParentI<*>,
|
||||||
@@ -31,7 +31,7 @@ class CommentVoter : Voter() {
|
|||||||
else -> granted()
|
else -> granted()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <S, CR : CitizenI> canUpdate(subject: S, citizen: CitizenI?): VoterResponse
|
fun <S, CR : CitizenI> canUpdate(subject: S, citizen: CitizenI?): AccessResponse
|
||||||
where S : CommentI,
|
where S : CommentI,
|
||||||
S : EntityCreatedBy<CR> = when {
|
S : EntityCreatedBy<CR> = when {
|
||||||
citizen == null -> denied("You must be connected to update comment", "comment.update.notConnected")
|
citizen == null -> denied("You must be connected to update comment", "comment.update.notConnected")
|
||||||
@@ -2,11 +2,11 @@ package fr.dcproject.component.comment.generic.routes
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
import fr.dcproject.component.comment.generic.CommentForUpdate
|
import fr.dcproject.component.comment.generic.CommentForUpdate
|
||||||
import fr.dcproject.component.comment.generic.CommentRef
|
import fr.dcproject.component.comment.generic.CommentRef
|
||||||
import fr.dcproject.component.comment.generic.CommentRepository
|
import fr.dcproject.component.comment.generic.CommentRepository
|
||||||
import fr.dcproject.component.comment.generic.CommentVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.features.NotFoundException
|
import io.ktor.features.NotFoundException
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
@@ -24,7 +24,7 @@ object CreateCommentChildren {
|
|||||||
class Input(val content: String)
|
class Input(val content: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.createCommentChildren(repo: CommentRepository, voter: CommentVoter) {
|
fun Route.createCommentChildren(repo: CommentRepository, ac: CommentAccessControl) {
|
||||||
post<CreateCommentChildrenRequest> {
|
post<CreateCommentChildrenRequest> {
|
||||||
val parent = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
|
val parent = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
|
||||||
val newComment = CommentForUpdate(
|
val newComment = CommentForUpdate(
|
||||||
@@ -33,7 +33,7 @@ object CreateCommentChildren {
|
|||||||
parent = parent
|
parent = parent
|
||||||
)
|
)
|
||||||
|
|
||||||
voter.assert { canCreate(newComment, citizenOrNull) }
|
ac.assert { canCreate(newComment, citizenOrNull) }
|
||||||
repo.comment(newComment)
|
repo.comment(newComment)
|
||||||
|
|
||||||
call.respond(HttpStatusCode.Created, newComment)
|
call.respond(HttpStatusCode.Created, newComment)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package fr.dcproject.component.comment.generic.routes
|
package fr.dcproject.component.comment.generic.routes
|
||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
import fr.dcproject.component.comment.generic.CommentRef
|
import fr.dcproject.component.comment.generic.CommentRef
|
||||||
import fr.dcproject.component.comment.generic.CommentRepository
|
import fr.dcproject.component.comment.generic.CommentRepository
|
||||||
import fr.dcproject.component.comment.generic.CommentVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.features.NotFoundException
|
import io.ktor.features.NotFoundException
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
@@ -20,10 +20,10 @@ object EditComment {
|
|||||||
@Location("/comments/{comment}")
|
@Location("/comments/{comment}")
|
||||||
class EditCommentRequest(val comment: CommentRef)
|
class EditCommentRequest(val comment: CommentRef)
|
||||||
|
|
||||||
fun Route.editComment(repo: CommentRepository, voter: CommentVoter) {
|
fun Route.editComment(repo: CommentRepository, ac: CommentAccessControl) {
|
||||||
put<EditCommentRequest> {
|
put<EditCommentRequest> {
|
||||||
val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
|
val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
|
||||||
voter.assert { canUpdate(comment, citizenOrNull) }
|
ac.assert { canUpdate(comment, citizenOrNull) }
|
||||||
|
|
||||||
comment.content = call.receiveText()
|
comment.content = call.receiveText()
|
||||||
repo.edit(comment)
|
repo.edit(comment)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package fr.dcproject.component.comment.generic.routes
|
package fr.dcproject.component.comment.generic.routes
|
||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
import fr.dcproject.component.comment.generic.CommentRepository
|
import fr.dcproject.component.comment.generic.CommentRepository
|
||||||
import fr.dcproject.component.comment.generic.CommentVoter
|
|
||||||
import fr.dcproject.routes.PaginatedRequest
|
import fr.dcproject.routes.PaginatedRequest
|
||||||
import fr.dcproject.routes.PaginatedRequestI
|
import fr.dcproject.routes.PaginatedRequestI
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -25,7 +25,7 @@ object GetCommentChildren {
|
|||||||
val search: String? = null
|
val search: String? = null
|
||||||
) : PaginatedRequestI by PaginatedRequest(page, limit)
|
) : PaginatedRequestI by PaginatedRequest(page, limit)
|
||||||
|
|
||||||
fun Route.getChildrenComments(repo: CommentRepository, voter: CommentVoter) {
|
fun Route.getChildrenComments(repo: CommentRepository, ac: CommentAccessControl) {
|
||||||
get<CommentChildrenRequest> {
|
get<CommentChildrenRequest> {
|
||||||
val comments =
|
val comments =
|
||||||
repo.findByParent(
|
repo.findByParent(
|
||||||
@@ -34,7 +34,7 @@ object GetCommentChildren {
|
|||||||
it.limit
|
it.limit
|
||||||
)
|
)
|
||||||
|
|
||||||
voter.assert { canView(comments.result, citizenOrNull) }
|
ac.assert { canView(comments.result, citizenOrNull) }
|
||||||
|
|
||||||
call.respond(HttpStatusCode.OK, comments)
|
call.respond(HttpStatusCode.OK, comments)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package fr.dcproject.component.comment.generic.routes
|
package fr.dcproject.component.comment.generic.routes
|
||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
import fr.dcproject.component.comment.generic.CommentRef
|
import fr.dcproject.component.comment.generic.CommentRef
|
||||||
import fr.dcproject.component.comment.generic.CommentRepository
|
import fr.dcproject.component.comment.generic.CommentRepository
|
||||||
import fr.dcproject.component.comment.generic.CommentVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.features.NotFoundException
|
import io.ktor.features.NotFoundException
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
@@ -19,10 +19,10 @@ object GetOneComment {
|
|||||||
@Location("/comments/{comment}")
|
@Location("/comments/{comment}")
|
||||||
class CommentRequest(val comment: CommentRef)
|
class CommentRequest(val comment: CommentRef)
|
||||||
|
|
||||||
fun Route.getOneComment(repo: CommentRepository, voter: CommentVoter) {
|
fun Route.getOneComment(repo: CommentRepository, ac: CommentAccessControl) {
|
||||||
get<CommentRequest> {
|
get<CommentRequest> {
|
||||||
val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment ${it.comment.id} not found")
|
val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment ${it.comment.id} not found")
|
||||||
voter.assert { canView(comment, citizenOrNull) }
|
ac.assert { canView(comment, citizenOrNull) }
|
||||||
|
|
||||||
call.respond(HttpStatusCode.OK, comment)
|
call.respond(HttpStatusCode.OK, comment)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
package fr.dcproject.component.follow
|
package fr.dcproject.component.follow
|
||||||
|
|
||||||
import fr.dcproject.component.citizen.CitizenI
|
import fr.dcproject.component.citizen.CitizenI
|
||||||
import fr.dcproject.voter.Voter
|
import fr.dcproject.security.AccessControl
|
||||||
import fr.dcproject.voter.VoterResponse
|
import fr.dcproject.security.AccessResponse
|
||||||
import fr.dcproject.component.follow.Follow as FollowEntity
|
import fr.dcproject.component.follow.Follow as FollowEntity
|
||||||
|
|
||||||
class FollowVoter : Voter() {
|
class FollowAccessControl : AccessControl() {
|
||||||
fun canCreate(subject: FollowI, citizen: CitizenI?): VoterResponse {
|
fun canCreate(subject: FollowI, citizen: CitizenI?): AccessResponse {
|
||||||
return if (citizen == null) denied("You must be connected to follow", "follow.create.notConnected")
|
return if (citizen == null) denied("You must be connected to follow", "follow.create.notConnected")
|
||||||
else granted()
|
else granted()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canDelete(subject: FollowI, citizen: CitizenI?): VoterResponse {
|
fun canDelete(subject: FollowI, citizen: CitizenI?): AccessResponse {
|
||||||
return if (citizen == null) denied("You must be connected to unfollow", "follow.delete.notConnected")
|
return if (citizen == null) denied("You must be connected to unfollow", "follow.delete.notConnected")
|
||||||
else granted()
|
else granted()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <S : FollowEntity<*>> canView(subjects: List<S>, citizen: CitizenI?): VoterResponse =
|
fun <S : FollowEntity<*>> canView(subjects: List<S>, citizen: CitizenI?): AccessResponse =
|
||||||
canAll(subjects) { canView(it, citizen) }
|
canAll(subjects) { canView(it, citizen) }
|
||||||
|
|
||||||
fun canView(subject: FollowEntity<*>, citizen: CitizenI?): VoterResponse {
|
fun canView(subject: FollowEntity<*>, citizen: CitizenI?): AccessResponse {
|
||||||
return if ((citizen != null && subject.createdBy.id == citizen.id) || !subject.createdBy.followAnonymous) granted()
|
return if ((citizen != null && subject.createdBy.id == citizen.id) || !subject.createdBy.followAnonymous) granted()
|
||||||
else denied("You cannot view an anonymous follow", "follow.view.anonymous")
|
else denied("You cannot view an anonymous follow", "follow.view.anonymous")
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,10 @@ package fr.dcproject.component.follow.routes.article
|
|||||||
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.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.FollowArticleRepository
|
import fr.dcproject.component.follow.FollowArticleRepository
|
||||||
import fr.dcproject.component.follow.FollowForUpdate
|
import fr.dcproject.component.follow.FollowForUpdate
|
||||||
import fr.dcproject.component.follow.FollowVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -20,10 +20,10 @@ object FollowArticle {
|
|||||||
@Location("/articles/{article}/follows")
|
@Location("/articles/{article}/follows")
|
||||||
class ArticleFollowRequest(val article: ArticleRef)
|
class ArticleFollowRequest(val article: ArticleRef)
|
||||||
|
|
||||||
fun Route.followArticle(repo: FollowArticleRepository, voter: FollowVoter) {
|
fun Route.followArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
|
||||||
post<ArticleFollowRequest> {
|
post<ArticleFollowRequest> {
|
||||||
val follow = FollowForUpdate(target = it.article, createdBy = this.citizen)
|
val follow = FollowForUpdate(target = it.article, createdBy = this.citizen)
|
||||||
voter.assert { canCreate(follow, citizenOrNull) }
|
ac.assert { canCreate(follow, citizenOrNull) }
|
||||||
repo.follow(follow)
|
repo.follow(follow)
|
||||||
call.respond(HttpStatusCode.Created)
|
call.respond(HttpStatusCode.Created)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package fr.dcproject.component.follow.routes.article
|
|||||||
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.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.FollowArticleRepository
|
import fr.dcproject.component.follow.FollowArticleRepository
|
||||||
import fr.dcproject.component.follow.FollowVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -19,10 +19,10 @@ object GetFollowArticle {
|
|||||||
@Location("/articles/{article}/follows")
|
@Location("/articles/{article}/follows")
|
||||||
class ArticleFollowRequest(val article: ArticleRef)
|
class ArticleFollowRequest(val article: ArticleRef)
|
||||||
|
|
||||||
fun Route.getFollowArticle(repo: FollowArticleRepository, voter: FollowVoter) {
|
fun Route.getFollowArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
|
||||||
get<ArticleFollowRequest> {
|
get<ArticleFollowRequest> {
|
||||||
repo.findFollow(citizen, it.article)?.let { follow ->
|
repo.findFollow(citizen, it.article)?.let { follow ->
|
||||||
voter.assert { canView(follow, citizenOrNull) }
|
ac.assert { canView(follow, citizenOrNull) }
|
||||||
call.respond(follow)
|
call.respond(follow)
|
||||||
} ?: call.respond(HttpStatusCode.NoContent)
|
} ?: call.respond(HttpStatusCode.NoContent)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package fr.dcproject.component.follow.routes.article
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.Citizen
|
import fr.dcproject.component.citizen.Citizen
|
||||||
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.FollowArticleRepository
|
import fr.dcproject.component.follow.FollowArticleRepository
|
||||||
import fr.dcproject.component.follow.FollowVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
import io.ktor.locations.Location
|
||||||
@@ -17,10 +17,10 @@ object GetMyFollowsArticle {
|
|||||||
@Location("/citizens/{citizen}/follows/articles")
|
@Location("/citizens/{citizen}/follows/articles")
|
||||||
class CitizenFollowArticleRequest(val citizen: Citizen)
|
class CitizenFollowArticleRequest(val citizen: Citizen)
|
||||||
|
|
||||||
fun Route.getMyFollowsArticle(repo: FollowArticleRepository, voter: FollowVoter) {
|
fun Route.getMyFollowsArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
|
||||||
get<CitizenFollowArticleRequest> {
|
get<CitizenFollowArticleRequest> {
|
||||||
val follows = repo.findByCitizen(it.citizen)
|
val follows = repo.findByCitizen(it.citizen)
|
||||||
voter.assert { canView(follows.result, citizenOrNull) }
|
ac.assert { canView(follows.result, citizenOrNull) }
|
||||||
call.respond(follows)
|
call.respond(follows)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package fr.dcproject.component.follow.routes.article
|
|||||||
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.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.FollowArticleRepository
|
import fr.dcproject.component.follow.FollowArticleRepository
|
||||||
import fr.dcproject.component.follow.FollowForUpdate
|
import fr.dcproject.component.follow.FollowForUpdate
|
||||||
import fr.dcproject.component.follow.FollowVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -20,10 +20,10 @@ object UnfollowArticle {
|
|||||||
@Location("/articles/{article}/follows")
|
@Location("/articles/{article}/follows")
|
||||||
class ArticleFollowRequest(val article: ArticleRef)
|
class ArticleFollowRequest(val article: ArticleRef)
|
||||||
|
|
||||||
fun Route.unfollowArticle(repo: FollowArticleRepository, voter: FollowVoter) {
|
fun Route.unfollowArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
|
||||||
delete<ArticleFollowRequest> {
|
delete<ArticleFollowRequest> {
|
||||||
val follow = FollowForUpdate(target = it.article, createdBy = this.citizen)
|
val follow = FollowForUpdate(target = it.article, createdBy = this.citizen)
|
||||||
voter.assert { canDelete(follow, citizenOrNull) }
|
ac.assert { canDelete(follow, citizenOrNull) }
|
||||||
repo.unfollow(follow)
|
repo.unfollow(follow)
|
||||||
call.respond(HttpStatusCode.NoContent)
|
call.respond(HttpStatusCode.NoContent)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package fr.dcproject.component.follow.routes.constitution
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.FollowConstitutionRepository
|
import fr.dcproject.component.follow.FollowConstitutionRepository
|
||||||
import fr.dcproject.component.follow.FollowForUpdate
|
import fr.dcproject.component.follow.FollowForUpdate
|
||||||
import fr.dcproject.component.follow.FollowVoter
|
|
||||||
import fr.dcproject.entity.ConstitutionRef
|
import fr.dcproject.entity.ConstitutionRef
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -20,10 +20,10 @@ object FollowConstitution {
|
|||||||
@Location("/constitutions/{constitution}/follows")
|
@Location("/constitutions/{constitution}/follows")
|
||||||
class ConstitutionFollowRequest(val constitution: ConstitutionRef)
|
class ConstitutionFollowRequest(val constitution: ConstitutionRef)
|
||||||
|
|
||||||
fun Route.followConstitution(repo: FollowConstitutionRepository, voter: FollowVoter) {
|
fun Route.followConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
|
||||||
post<ConstitutionFollowRequest> {
|
post<ConstitutionFollowRequest> {
|
||||||
val follow = FollowForUpdate(target = it.constitution, createdBy = this.citizen)
|
val follow = FollowForUpdate(target = it.constitution, createdBy = this.citizen)
|
||||||
voter.assert { canCreate(follow, citizenOrNull) }
|
ac.assert { canCreate(follow, citizenOrNull) }
|
||||||
repo.follow(follow)
|
repo.follow(follow)
|
||||||
call.respond(HttpStatusCode.Created)
|
call.respond(HttpStatusCode.Created)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ package fr.dcproject.component.follow.routes.constitution
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.FollowConstitutionRepository
|
import fr.dcproject.component.follow.FollowConstitutionRepository
|
||||||
import fr.dcproject.component.follow.FollowVoter
|
|
||||||
import fr.dcproject.entity.ConstitutionRef
|
import fr.dcproject.entity.ConstitutionRef
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -19,10 +19,10 @@ object GetFollowConstitution {
|
|||||||
@Location("/constitutions/{constitution}/follows")
|
@Location("/constitutions/{constitution}/follows")
|
||||||
class ConstitutionFollowRequest(val constitution: ConstitutionRef)
|
class ConstitutionFollowRequest(val constitution: ConstitutionRef)
|
||||||
|
|
||||||
fun Route.getFollowConstitution(repo: FollowConstitutionRepository, voter: FollowVoter) {
|
fun Route.getFollowConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
|
||||||
get<ConstitutionFollowRequest> {
|
get<ConstitutionFollowRequest> {
|
||||||
repo.findFollow(citizen, it.constitution)?.let { follow ->
|
repo.findFollow(citizen, it.constitution)?.let { follow ->
|
||||||
voter.assert { canView(follow, citizenOrNull) }
|
ac.assert { canView(follow, citizenOrNull) }
|
||||||
call.respond(follow)
|
call.respond(follow)
|
||||||
} ?: call.respond(HttpStatusCode.NotFound)
|
} ?: call.respond(HttpStatusCode.NotFound)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package fr.dcproject.component.follow.routes.constitution
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.CitizenRef
|
import fr.dcproject.component.citizen.CitizenRef
|
||||||
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.FollowConstitutionRepository
|
import fr.dcproject.component.follow.FollowConstitutionRepository
|
||||||
import fr.dcproject.component.follow.FollowVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
import io.ktor.locations.Location
|
||||||
@@ -17,10 +17,10 @@ object GetMyFollowsConstitution {
|
|||||||
@Location("/citizens/{citizen}/follows/constitutions")
|
@Location("/citizens/{citizen}/follows/constitutions")
|
||||||
class CitizenFollowConstitutionRequest(val citizen: CitizenRef)
|
class CitizenFollowConstitutionRequest(val citizen: CitizenRef)
|
||||||
|
|
||||||
fun Route.getMyFollowsConstitution(repo: FollowConstitutionRepository, voter: FollowVoter) {
|
fun Route.getMyFollowsConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
|
||||||
get<CitizenFollowConstitutionRequest> {
|
get<CitizenFollowConstitutionRequest> {
|
||||||
val follows = repo.findByCitizen(it.citizen)
|
val follows = repo.findByCitizen(it.citizen)
|
||||||
voter.assert { canView(follows.result, citizenOrNull) }
|
ac.assert { canView(follows.result, citizenOrNull) }
|
||||||
call.respond(follows)
|
call.respond(follows)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package fr.dcproject.component.follow.routes.constitution
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.FollowConstitutionRepository
|
import fr.dcproject.component.follow.FollowConstitutionRepository
|
||||||
import fr.dcproject.component.follow.FollowForUpdate
|
import fr.dcproject.component.follow.FollowForUpdate
|
||||||
import fr.dcproject.component.follow.FollowVoter
|
|
||||||
import fr.dcproject.entity.ConstitutionRef
|
import fr.dcproject.entity.ConstitutionRef
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -20,10 +20,10 @@ object UnfollowConstitution {
|
|||||||
@Location("/constitutions/{constitution}/follows")
|
@Location("/constitutions/{constitution}/follows")
|
||||||
class ConstitutionUnfollowRequest(val constitution: ConstitutionRef)
|
class ConstitutionUnfollowRequest(val constitution: ConstitutionRef)
|
||||||
|
|
||||||
fun Route.unfollowConstitution(repo: FollowConstitutionRepository, voter: FollowVoter) {
|
fun Route.unfollowConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
|
||||||
delete<ConstitutionUnfollowRequest> {
|
delete<ConstitutionUnfollowRequest> {
|
||||||
val follow = FollowForUpdate(target = it.constitution, createdBy = this.citizen)
|
val follow = FollowForUpdate(target = it.constitution, createdBy = this.citizen)
|
||||||
voter.assert { canDelete(follow, citizenOrNull) }
|
ac.assert { canDelete(follow, citizenOrNull) }
|
||||||
repo.unfollow(follow)
|
repo.unfollow(follow)
|
||||||
call.respond(HttpStatusCode.NoContent)
|
call.respond(HttpStatusCode.NoContent)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,17 +3,17 @@ package fr.dcproject.component.opinion
|
|||||||
import fr.dcproject.component.citizen.CitizenI
|
import fr.dcproject.component.citizen.CitizenI
|
||||||
import fr.dcproject.component.opinion.entity.OpinionI
|
import fr.dcproject.component.opinion.entity.OpinionI
|
||||||
import fr.dcproject.entity.HasTarget
|
import fr.dcproject.entity.HasTarget
|
||||||
import fr.dcproject.voter.Voter
|
import fr.dcproject.security.AccessControl
|
||||||
import fr.dcproject.voter.VoterResponse
|
import fr.dcproject.security.AccessResponse
|
||||||
import fr.postgresjson.entity.EntityCreatedBy
|
import fr.postgresjson.entity.EntityCreatedBy
|
||||||
import fr.postgresjson.entity.EntityDeletedAt
|
import fr.postgresjson.entity.EntityDeletedAt
|
||||||
|
|
||||||
class OpinionVoter : Voter() {
|
class OpinionAccessControl : AccessControl() {
|
||||||
|
|
||||||
fun <S> canCreate(subjects: List<S>, citizen: CitizenI?): VoterResponse where S : OpinionI, S : HasTarget<*> =
|
fun <S> canCreate(subjects: List<S>, citizen: CitizenI?): AccessResponse where S : OpinionI, S : HasTarget<*> =
|
||||||
canAll(subjects) { canCreate(it, citizen) }
|
canAll(subjects) { canCreate(it, citizen) }
|
||||||
|
|
||||||
fun <S> canCreate(subject: S, citizen: CitizenI?): VoterResponse where S : OpinionI, S : HasTarget<*> {
|
fun <S> canCreate(subject: S, citizen: CitizenI?): AccessResponse where S : OpinionI, S : HasTarget<*> {
|
||||||
val target = subject.target
|
val target = subject.target
|
||||||
return when {
|
return when {
|
||||||
citizen == null -> denied("You must be connected to make an opinion", "opinion.create.notConnected")
|
citizen == null -> denied("You must be connected to make an opinion", "opinion.create.notConnected")
|
||||||
@@ -22,12 +22,12 @@ class OpinionVoter : Voter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <S : OpinionI, SS : List<S>> canView(subjects: SS, citizen: CitizenI?): VoterResponse =
|
fun <S : OpinionI, SS : List<S>> canView(subjects: SS, citizen: CitizenI?): AccessResponse =
|
||||||
canAll(subjects) { canView(it, citizen) }
|
canAll(subjects) { canView(it, citizen) }
|
||||||
|
|
||||||
fun <S : OpinionI> canView(subject: S, citizen: CitizenI?): VoterResponse = granted()
|
fun <S : OpinionI> canView(subject: S, citizen: CitizenI?): AccessResponse = granted()
|
||||||
|
|
||||||
fun <S, C : CitizenI> canDelete(subject: S, citizen: CitizenI?): VoterResponse where S : EntityCreatedBy<C>, S : OpinionI = when {
|
fun <S, C : CitizenI> canDelete(subject: S, citizen: CitizenI?): AccessResponse where S : EntityCreatedBy<C>, S : OpinionI = when {
|
||||||
citizen == null -> denied("You must be connected to delete opinion", "opinion.delete.notConnected")
|
citizen == null -> denied("You must be connected to delete opinion", "opinion.delete.notConnected")
|
||||||
subject.createdBy.id != citizen.id -> denied("You can only delete your opinions", "opinion.delete.notYours")
|
subject.createdBy.id != citizen.id -> denied("You can only delete your opinions", "opinion.delete.notYours")
|
||||||
else -> granted()
|
else -> granted()
|
||||||
@@ -2,14 +2,14 @@ package fr.dcproject.component.opinion
|
|||||||
|
|
||||||
import fr.dcproject.component.citizen.CitizenI
|
import fr.dcproject.component.citizen.CitizenI
|
||||||
import fr.dcproject.component.opinion.entity.OpinionChoice
|
import fr.dcproject.component.opinion.entity.OpinionChoice
|
||||||
import fr.dcproject.voter.Voter
|
import fr.dcproject.security.AccessControl
|
||||||
import fr.dcproject.voter.VoterResponse
|
import fr.dcproject.security.AccessResponse
|
||||||
|
|
||||||
class OpinionChoiceVoter : Voter() {
|
class OpinionChoiceAccessControl : AccessControl() {
|
||||||
fun canView(subjects: List<OpinionChoice>, citizen: CitizenI?): VoterResponse =
|
fun canView(subjects: List<OpinionChoice>, citizen: CitizenI?): AccessResponse =
|
||||||
canAll(subjects) { canView(it, citizen) }
|
canAll(subjects) { canView(it, citizen) }
|
||||||
|
|
||||||
fun canView(subject: OpinionChoice, citizen: CitizenI?): VoterResponse {
|
fun canView(subject: OpinionChoice, citizen: CitizenI?): AccessResponse {
|
||||||
return granted()
|
return granted()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,10 @@ package fr.dcproject.component.opinion.routes
|
|||||||
|
|
||||||
import fr.dcproject.component.article.ArticleRef
|
import fr.dcproject.component.article.ArticleRef
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.opinion.OpinionVoter
|
import fr.dcproject.component.opinion.OpinionAccessControl
|
||||||
import fr.dcproject.component.opinion.entity.Opinion
|
import fr.dcproject.component.opinion.entity.Opinion
|
||||||
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.utils.toUUID
|
import fr.dcproject.utils.toUUID
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
import io.ktor.locations.Location
|
||||||
@@ -27,10 +27,10 @@ object GetCitizenOpinions {
|
|||||||
val id: List<UUID> = id.toUUID()
|
val id: List<UUID> = id.toUUID()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.getCitizenOpinions(repo: OpinionArticleRepository, voter: OpinionVoter) {
|
fun Route.getCitizenOpinions(repo: OpinionArticleRepository, ac: OpinionAccessControl) {
|
||||||
get<CitizenOpinions> {
|
get<CitizenOpinions> {
|
||||||
val opinionsEntities: List<Opinion<ArticleRef>> = repo.findCitizenOpinionsByTargets(it.citizen, it.id)
|
val opinionsEntities: List<Opinion<ArticleRef>> = repo.findCitizenOpinionsByTargets(it.citizen, it.id)
|
||||||
voter.assert { canView(opinionsEntities, citizenOrNull) }
|
ac.assert { canView(opinionsEntities, citizenOrNull) }
|
||||||
|
|
||||||
call.respond(opinionsEntities)
|
call.respond(opinionsEntities)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package fr.dcproject.component.opinion.routes
|
|||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.CitizenRef
|
import fr.dcproject.component.citizen.CitizenRef
|
||||||
import fr.dcproject.component.opinion.OpinionVoter
|
import fr.dcproject.component.opinion.OpinionAccessControl
|
||||||
import fr.dcproject.routes.PaginatedRequest
|
import fr.dcproject.routes.PaginatedRequest
|
||||||
import fr.dcproject.routes.PaginatedRequestI
|
import fr.dcproject.routes.PaginatedRequestI
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
import io.ktor.locations.Location
|
||||||
@@ -27,10 +27,10 @@ object GetMyOpinionsArticle {
|
|||||||
limit: Int = 50
|
limit: Int = 50
|
||||||
) : PaginatedRequestI by PaginatedRequest(page, limit)
|
) : PaginatedRequestI by PaginatedRequest(page, limit)
|
||||||
|
|
||||||
fun Route.getMyOpinionsArticle(repo: OpinionArticleRepository, voter: OpinionVoter) {
|
fun Route.getMyOpinionsArticle(repo: OpinionArticleRepository, ac: OpinionAccessControl) {
|
||||||
get<CitizenOpinionsArticleRequest> {
|
get<CitizenOpinionsArticleRequest> {
|
||||||
val opinions = repo.findCitizenOpinions(citizen, it.page, it.limit)
|
val opinions = repo.findCitizenOpinions(citizen, it.page, it.limit)
|
||||||
voter.assert { canView(opinions.result, citizenOrNull) }
|
ac.assert { canView(opinions.result, citizenOrNull) }
|
||||||
call.respond(opinions)
|
call.respond(opinions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package fr.dcproject.component.opinion.routes
|
package fr.dcproject.component.opinion.routes
|
||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.opinion.OpinionChoiceVoter
|
import fr.dcproject.component.opinion.OpinionChoiceAccessControl
|
||||||
import fr.dcproject.component.opinion.entity.OpinionChoice
|
import fr.dcproject.component.opinion.entity.OpinionChoice
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
import io.ktor.locations.Location
|
||||||
@@ -16,9 +16,9 @@ object GetOpinionChoice {
|
|||||||
@Location("/opinions/{opinionChoice}")
|
@Location("/opinions/{opinionChoice}")
|
||||||
class OpinionChoiceRequest(val opinionChoice: OpinionChoice)
|
class OpinionChoiceRequest(val opinionChoice: OpinionChoice)
|
||||||
|
|
||||||
fun Route.getOpinionChoice(voter: OpinionChoiceVoter) {
|
fun Route.getOpinionChoice(ac: OpinionChoiceAccessControl) {
|
||||||
get<OpinionChoiceRequest> {
|
get<OpinionChoiceRequest> {
|
||||||
voter.assert { canView(it.opinionChoice, citizenOrNull) }
|
ac.assert { canView(it.opinionChoice, citizenOrNull) }
|
||||||
|
|
||||||
call.respond(it.opinionChoice)
|
call.respond(it.opinionChoice)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package fr.dcproject.component.opinion.routes
|
package fr.dcproject.component.opinion.routes
|
||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.opinion.OpinionChoiceAccessControl
|
||||||
import fr.dcproject.component.opinion.OpinionChoiceRepository
|
import fr.dcproject.component.opinion.OpinionChoiceRepository
|
||||||
import fr.dcproject.component.opinion.OpinionChoiceVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
import io.ktor.locations.Location
|
||||||
@@ -16,10 +16,10 @@ object GetOpinionChoices {
|
|||||||
@Location("/opinions")
|
@Location("/opinions")
|
||||||
class OpinionChoicesRequest(val targets: List<String> = emptyList())
|
class OpinionChoicesRequest(val targets: List<String> = emptyList())
|
||||||
|
|
||||||
fun Route.getOpinionChoices(repo: OpinionChoiceRepository, voter: OpinionChoiceVoter) {
|
fun Route.getOpinionChoices(repo: OpinionChoiceRepository, ac: OpinionChoiceAccessControl) {
|
||||||
get<OpinionChoicesRequest> {
|
get<OpinionChoicesRequest> {
|
||||||
val opinionChoices = repo.findOpinionsChoices(it.targets)
|
val opinionChoices = repo.findOpinionsChoices(it.targets)
|
||||||
voter.assert { canView(opinionChoices, citizenOrNull) }
|
ac.assert { canView(opinionChoices, citizenOrNull) }
|
||||||
|
|
||||||
call.respond(opinionChoices)
|
call.respond(opinionChoices)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ package fr.dcproject.component.opinion.routes
|
|||||||
import fr.dcproject.component.article.ArticleForView
|
import fr.dcproject.component.article.ArticleForView
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.opinion.OpinionVoter
|
import fr.dcproject.component.opinion.OpinionAccessControl
|
||||||
import fr.dcproject.component.opinion.entity.OpinionChoiceRef
|
import fr.dcproject.component.opinion.entity.OpinionChoiceRef
|
||||||
import fr.dcproject.component.opinion.entity.OpinionForUpdate
|
import fr.dcproject.component.opinion.entity.OpinionForUpdate
|
||||||
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.utils.toUUID
|
import fr.dcproject.utils.toUUID
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -31,7 +31,7 @@ object OpinionArticle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.setOpinionOnArticle(repo: OpinionArticleRepository, voter: OpinionVoter) {
|
fun Route.setOpinionOnArticle(repo: OpinionArticleRepository, ac: OpinionAccessControl) {
|
||||||
put<ArticleOpinion> {
|
put<ArticleOpinion> {
|
||||||
call.receive<ArticleOpinion.Body>().ids.map { id ->
|
call.receive<ArticleOpinion.Body>().ids.map { id ->
|
||||||
OpinionForUpdate(
|
OpinionForUpdate(
|
||||||
@@ -40,7 +40,7 @@ object OpinionArticle {
|
|||||||
createdBy = citizen
|
createdBy = citizen
|
||||||
)
|
)
|
||||||
}.let { opinions ->
|
}.let { opinions ->
|
||||||
voter.assert { canCreate(opinions, citizenOrNull) }
|
ac.assert { canCreate(opinions, citizenOrNull) }
|
||||||
repo.updateOpinions(opinions)
|
repo.updateOpinions(opinions)
|
||||||
}.let {
|
}.let {
|
||||||
call.respond(HttpStatusCode.Created, it)
|
call.respond(HttpStatusCode.Created, it)
|
||||||
|
|||||||
@@ -3,22 +3,22 @@ package fr.dcproject.component.vote
|
|||||||
import fr.dcproject.component.citizen.CitizenI
|
import fr.dcproject.component.citizen.CitizenI
|
||||||
import fr.dcproject.component.vote.entity.VoteForUpdateI
|
import fr.dcproject.component.vote.entity.VoteForUpdateI
|
||||||
import fr.dcproject.entity.TargetI
|
import fr.dcproject.entity.TargetI
|
||||||
import fr.dcproject.voter.Voter
|
import fr.dcproject.security.AccessControl
|
||||||
import fr.dcproject.voter.VoterResponse
|
import fr.dcproject.security.AccessResponse
|
||||||
import fr.postgresjson.entity.EntityDeletedAt
|
import fr.postgresjson.entity.EntityDeletedAt
|
||||||
import fr.dcproject.component.vote.entity.Vote as VoteEntity
|
import fr.dcproject.component.vote.entity.Vote as VoteEntity
|
||||||
|
|
||||||
class VoteVoter : Voter() {
|
class VoteAccessControl : AccessControl() {
|
||||||
fun <S> canCreate(subject: VoteForUpdateI<S, *>, citizen: CitizenI?): VoterResponse where S : EntityDeletedAt, S : TargetI = when {
|
fun <S> canCreate(subject: VoteForUpdateI<S, *>, citizen: CitizenI?): AccessResponse where S : EntityDeletedAt, S : TargetI = when {
|
||||||
citizen == null -> denied("You must be connected for vote", "vote.create.connected")
|
citizen == null -> denied("You must be connected for vote", "vote.create.connected")
|
||||||
subject.target.isDeleted() -> denied("You cannot vote on deleted target", "vote.create.isDeleted")
|
subject.target.isDeleted() -> denied("You cannot vote on deleted target", "vote.create.isDeleted")
|
||||||
else -> granted()
|
else -> granted()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <S : VoteEntity<*>> canView(subjects: List<S>, citizen: CitizenI?): VoterResponse =
|
fun <S : VoteEntity<*>> canView(subjects: List<S>, citizen: CitizenI?): AccessResponse =
|
||||||
canAll(subjects) { canView(it, citizen) }
|
canAll(subjects) { canView(it, citizen) }
|
||||||
|
|
||||||
fun canView(subject: VoteEntity<*>, citizen: CitizenI?): VoterResponse = when {
|
fun canView(subject: VoteEntity<*>, citizen: CitizenI?): AccessResponse = when {
|
||||||
citizen == null -> denied("You must be connected for view your votes", "vote.view.connected")
|
citizen == null -> denied("You must be connected for view your votes", "vote.view.connected")
|
||||||
subject.createdBy.id != citizen.id -> denied("You can only display your votes", "vote.view.onlyYours")
|
subject.createdBy.id != citizen.id -> denied("You can only display your votes", "vote.view.onlyYours")
|
||||||
else -> granted()
|
else -> granted()
|
||||||
@@ -2,10 +2,10 @@ package fr.dcproject.component.vote.routes
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.Citizen
|
import fr.dcproject.component.citizen.Citizen
|
||||||
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
import fr.dcproject.component.vote.VoteRepository
|
import fr.dcproject.component.vote.VoteRepository
|
||||||
import fr.dcproject.component.vote.VoteVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.utils.toUUID
|
import fr.dcproject.utils.toUUID
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
import io.ktor.locations.Location
|
||||||
@@ -21,11 +21,11 @@ object GetCitizenVotes {
|
|||||||
val id: List<UUID> = id.toUUID()
|
val id: List<UUID> = id.toUUID()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.getCitizenVote(repo: VoteRepository, voter: VoteVoter) {
|
fun Route.getCitizenVote(repo: VoteRepository, ac: VoteAccessControl) {
|
||||||
get<CitizenVotesRequest> {
|
get<CitizenVotesRequest> {
|
||||||
val votes = repo.findCitizenVotesByTargets(it.citizen, it.id)
|
val votes = repo.findCitizenVotesByTargets(it.citizen, it.id)
|
||||||
if (votes.isNotEmpty()) {
|
if (votes.isNotEmpty()) {
|
||||||
voter.assert { canView(votes, citizenOrNull) }
|
ac.assert { canView(votes, citizenOrNull) }
|
||||||
}
|
}
|
||||||
call.respond(votes)
|
call.respond(votes)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package fr.dcproject.component.vote.routes
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.Citizen
|
import fr.dcproject.component.citizen.Citizen
|
||||||
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
import fr.dcproject.component.vote.VoteArticleRepository
|
import fr.dcproject.component.vote.VoteArticleRepository
|
||||||
import fr.dcproject.component.vote.VoteVoter
|
|
||||||
import fr.dcproject.routes.PaginatedRequest
|
import fr.dcproject.routes.PaginatedRequest
|
||||||
import fr.dcproject.routes.PaginatedRequestI
|
import fr.dcproject.routes.PaginatedRequestI
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
import io.ktor.locations.Location
|
||||||
@@ -24,10 +24,10 @@ object GetCitizenVotesOnArticle {
|
|||||||
val search: String? = null
|
val search: String? = null
|
||||||
) : PaginatedRequestI by PaginatedRequest(page, limit)
|
) : PaginatedRequestI by PaginatedRequest(page, limit)
|
||||||
|
|
||||||
fun Route.getCitizenVotesOnArticle(repo: VoteArticleRepository, voter: VoteVoter) {
|
fun Route.getCitizenVotesOnArticle(repo: VoteArticleRepository, ac: VoteAccessControl) {
|
||||||
get<CitizenVoteArticleRequest> {
|
get<CitizenVoteArticleRequest> {
|
||||||
val votes = repo.findByCitizen(it.citizen, it.page, it.limit)
|
val votes = repo.findByCitizen(it.citizen, it.page, it.limit)
|
||||||
voter.assert { canView(votes.result, citizenOrNull) }
|
ac.assert { canView(votes.result, citizenOrNull) }
|
||||||
|
|
||||||
call.respond(votes)
|
call.respond(votes)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package fr.dcproject.component.vote.routes
|
|||||||
import fr.dcproject.component.article.ArticleForView
|
import fr.dcproject.component.article.ArticleForView
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
import fr.dcproject.component.vote.VoteArticleRepository
|
import fr.dcproject.component.vote.VoteArticleRepository
|
||||||
import fr.dcproject.component.vote.VoteVoter
|
|
||||||
import fr.dcproject.component.vote.entity.VoteForUpdate
|
import fr.dcproject.component.vote.entity.VoteForUpdate
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -23,7 +23,7 @@ object PutVoteOnArticle {
|
|||||||
data class Content(var note: Int)
|
data class Content(var note: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.putVoteOnArticle(repo: VoteArticleRepository, voter: VoteVoter) {
|
fun Route.putVoteOnArticle(repo: VoteArticleRepository, ac: VoteAccessControl) {
|
||||||
put<ArticleVoteRequest> {
|
put<ArticleVoteRequest> {
|
||||||
val content = call.receive<ArticleVoteRequest.Content>()
|
val content = call.receive<ArticleVoteRequest.Content>()
|
||||||
val vote = VoteForUpdate(
|
val vote = VoteForUpdate(
|
||||||
@@ -31,7 +31,7 @@ object PutVoteOnArticle {
|
|||||||
note = content.note,
|
note = content.note,
|
||||||
createdBy = this.citizen
|
createdBy = this.citizen
|
||||||
)
|
)
|
||||||
voter.assert { canCreate(vote, citizenOrNull) }
|
ac.assert { canCreate(vote, citizenOrNull) }
|
||||||
val votes = repo.vote(vote)
|
val votes = repo.vote(vote)
|
||||||
call.respond(HttpStatusCode.Created, votes)
|
call.respond(HttpStatusCode.Created, votes)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package fr.dcproject.component.vote.routes
|
|||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.comment.generic.CommentRepository
|
import fr.dcproject.component.comment.generic.CommentRepository
|
||||||
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
import fr.dcproject.component.vote.VoteCommentRepository
|
import fr.dcproject.component.vote.VoteCommentRepository
|
||||||
import fr.dcproject.component.vote.VoteVoter
|
|
||||||
import fr.dcproject.component.vote.entity.VoteForUpdate
|
import fr.dcproject.component.vote.entity.VoteForUpdate
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -24,7 +24,7 @@ object PutVoteOnComment {
|
|||||||
data class Content(var note: Int)
|
data class Content(var note: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.putVoteOnComment(voteCommentRepo: VoteCommentRepository, commentRepo: CommentRepository, voter: VoteVoter) {
|
fun Route.putVoteOnComment(voteCommentRepo: VoteCommentRepository, commentRepo: CommentRepository, ac: VoteAccessControl) {
|
||||||
put<CommentVoteRequest> {
|
put<CommentVoteRequest> {
|
||||||
val comment = commentRepo.findById(it.comment)!!
|
val comment = commentRepo.findById(it.comment)!!
|
||||||
val content = call.receive<CommentVoteRequest.Content>()
|
val content = call.receive<CommentVoteRequest.Content>()
|
||||||
@@ -33,7 +33,7 @@ object PutVoteOnComment {
|
|||||||
note = content.note,
|
note = content.note,
|
||||||
createdBy = this.citizen
|
createdBy = this.citizen
|
||||||
)
|
)
|
||||||
voter.assert { canCreate(vote, citizenOrNull) }
|
ac.assert { canCreate(vote, citizenOrNull) }
|
||||||
val votes = voteCommentRepo.vote(vote)
|
val votes = voteCommentRepo.vote(vote)
|
||||||
call.respond(HttpStatusCode.Created, votes)
|
call.respond(HttpStatusCode.Created, votes)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package fr.dcproject.component.vote.routes
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
import fr.dcproject.component.vote.VoteConstitutionRepository
|
import fr.dcproject.component.vote.VoteConstitutionRepository
|
||||||
import fr.dcproject.component.vote.VoteVoter
|
|
||||||
import fr.dcproject.component.vote.entity.VoteForUpdate
|
import fr.dcproject.component.vote.entity.VoteForUpdate
|
||||||
import fr.dcproject.component.vote.routes.VoteConstitution.ConstitutionVoteRequest.Input
|
import fr.dcproject.component.vote.routes.VoteConstitution.ConstitutionVoteRequest.Input
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -24,7 +24,7 @@ object VoteConstitution {
|
|||||||
data class Input(var note: Int)
|
data class Input(var note: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.voteConstitution(repo: VoteConstitutionRepository, voter: VoteVoter) {
|
fun Route.voteConstitution(repo: VoteConstitutionRepository, ac: VoteAccessControl) {
|
||||||
put<ConstitutionVoteRequest> {
|
put<ConstitutionVoteRequest> {
|
||||||
val content = call.receive<Input>()
|
val content = call.receive<Input>()
|
||||||
val vote = VoteForUpdate(
|
val vote = VoteForUpdate(
|
||||||
@@ -32,7 +32,7 @@ object VoteConstitution {
|
|||||||
note = content.note,
|
note = content.note,
|
||||||
createdBy = this.citizen
|
createdBy = this.citizen
|
||||||
)
|
)
|
||||||
voter.assert { canCreate(vote, citizenOrNull) }
|
ac.assert { canCreate(vote, citizenOrNull) }
|
||||||
repo.vote(vote)
|
repo.vote(vote)
|
||||||
call.respond(HttpStatusCode.Created)
|
call.respond(HttpStatusCode.Created)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,48 +2,48 @@ package fr.dcproject.component.workgroup
|
|||||||
|
|
||||||
import fr.dcproject.component.citizen.CitizenI
|
import fr.dcproject.component.citizen.CitizenI
|
||||||
import fr.dcproject.component.workgroup.WorkgroupWithMembersI.Member.Role
|
import fr.dcproject.component.workgroup.WorkgroupWithMembersI.Member.Role
|
||||||
import fr.dcproject.voter.Voter
|
import fr.dcproject.security.AccessControl
|
||||||
import fr.dcproject.voter.VoterResponse
|
import fr.dcproject.security.AccessResponse
|
||||||
|
|
||||||
class WorkgroupVoter : Voter() {
|
class WorkgroupAccessControl : AccessControl() {
|
||||||
fun canCreate(subject: WorkgroupI, citizen: CitizenI?): VoterResponse {
|
fun canCreate(subject: WorkgroupI, citizen: CitizenI?): AccessResponse {
|
||||||
if (citizen == null) return denied("You must be connected to create workgroup", "workgroup.create.notConnected")
|
if (citizen == null) return denied("You must be connected to create workgroup", "workgroup.create.notConnected")
|
||||||
return granted()
|
return granted()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <S : WorkgroupWithAuthI<*>> canView(subjects: List<S>, citizen: CitizenI?): VoterResponse =
|
fun <S : WorkgroupWithAuthI<*>> canView(subjects: List<S>, citizen: CitizenI?): AccessResponse =
|
||||||
canAll(subjects) { canView(it, citizen) }
|
canAll(subjects) { canView(it, citizen) }
|
||||||
|
|
||||||
fun canView(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): VoterResponse =
|
fun canView(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): AccessResponse =
|
||||||
if (subject.isDeleted()) denied("You cannot view a deleted workgroup", "workgroup.view.deleted")
|
if (subject.isDeleted()) denied("You cannot view a deleted workgroup", "workgroup.view.deleted")
|
||||||
else if (!subject.anonymous) granted()
|
else if (!subject.anonymous) granted()
|
||||||
else if (subject.anonymous && citizen != null && subject.isMember(citizen)) granted()
|
else if (subject.anonymous && citizen != null && subject.isMember(citizen)) granted()
|
||||||
else denied("You cannot view anonymous workgroup", "workgroup.view.anonymous")
|
else denied("You cannot view anonymous workgroup", "workgroup.view.anonymous")
|
||||||
|
|
||||||
fun canDelete(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): VoterResponse {
|
fun canDelete(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): AccessResponse {
|
||||||
if (citizen == null) return denied("You must be connected to delete workgroup", "workgroup.delete.notConnected")
|
if (citizen == null) return denied("You must be connected to delete workgroup", "workgroup.delete.notConnected")
|
||||||
return if (subject.hasRole(Role.MASTER, citizen)) granted()
|
return if (subject.hasRole(Role.MASTER, citizen)) granted()
|
||||||
else denied("You must hase role MASTER to delete workgroup", "workgroup.delete.role")
|
else denied("You must hase role MASTER to delete workgroup", "workgroup.delete.role")
|
||||||
}
|
}
|
||||||
fun canUpdate(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): VoterResponse {
|
fun canUpdate(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): AccessResponse {
|
||||||
if (citizen == null) return denied("You must be connected to update workgroup", "workgroup.update.notConnected")
|
if (citizen == null) return denied("You must be connected to update workgroup", "workgroup.update.notConnected")
|
||||||
return if (subject.hasRole(Role.MASTER, citizen)) granted()
|
return if (subject.hasRole(Role.MASTER, citizen)) granted()
|
||||||
else denied("You must hase role MASTER to delete workgroup", "workgroup.delete.role")
|
else denied("You must hase role MASTER to delete workgroup", "workgroup.delete.role")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canAddMembers(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): VoterResponse = when {
|
fun canAddMembers(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): AccessResponse = when {
|
||||||
citizen == null -> denied("You must be connected to add member to the workgroup", "workgroup.addMember.notConnected")
|
citizen == null -> denied("You must be connected to add member to the workgroup", "workgroup.addMember.notConnected")
|
||||||
subject.hasRole(Role.MASTER, citizen) -> granted()
|
subject.hasRole(Role.MASTER, citizen) -> granted()
|
||||||
else -> denied("You must have MASTER Role for add member to workgroup", "workgroup.addMember.role")
|
else -> denied("You must have MASTER Role for add member to workgroup", "workgroup.addMember.role")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canUpdateMembers(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): VoterResponse = when {
|
fun canUpdateMembers(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): AccessResponse = when {
|
||||||
citizen == null -> denied("You must be connected to update member of the workgroup", "workgroup.updateMember.notConnected")
|
citizen == null -> denied("You must be connected to update member of the workgroup", "workgroup.updateMember.notConnected")
|
||||||
subject.hasRole(Role.MASTER, citizen) -> granted()
|
subject.hasRole(Role.MASTER, citizen) -> granted()
|
||||||
else -> denied("You must have MASTER Role for update members of workgroup", "workgroup.updateMember.role")
|
else -> denied("You must have MASTER Role for update members of workgroup", "workgroup.updateMember.role")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canRemoveMembers(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): VoterResponse = when {
|
fun canRemoveMembers(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): AccessResponse = when {
|
||||||
citizen == null -> denied("You must be connected to remove member of the workgroup", "workgroup.removeMember.notConnected")
|
citizen == null -> denied("You must be connected to remove member of the workgroup", "workgroup.removeMember.notConnected")
|
||||||
subject.hasRole(Role.MASTER, citizen) -> granted()
|
subject.hasRole(Role.MASTER, citizen) -> granted()
|
||||||
else -> denied("You must have MASTER Role for remove members of workgroup", "workgroup.removeMember.role")
|
else -> denied("You must have MASTER Role for remove members of workgroup", "workgroup.removeMember.role")
|
||||||
@@ -2,11 +2,11 @@ package fr.dcproject.component.workgroup.routes
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.WorkgroupRepository
|
import fr.dcproject.component.workgroup.WorkgroupRepository
|
||||||
import fr.dcproject.component.workgroup.WorkgroupSimple
|
import fr.dcproject.component.workgroup.WorkgroupSimple
|
||||||
import fr.dcproject.component.workgroup.WorkgroupVoter
|
|
||||||
import fr.dcproject.component.workgroup.routes.CreateWorkgroup.PostWorkgroupRequest.Input
|
import fr.dcproject.component.workgroup.routes.CreateWorkgroup.PostWorkgroupRequest.Input
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -30,7 +30,7 @@ object CreateWorkgroup {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.createWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
|
fun Route.createWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
post<PostWorkgroupRequest> {
|
post<PostWorkgroupRequest> {
|
||||||
call.receive<Input>().run {
|
call.receive<Input>().run {
|
||||||
WorkgroupSimple(
|
WorkgroupSimple(
|
||||||
@@ -42,7 +42,7 @@ object CreateWorkgroup {
|
|||||||
citizen
|
citizen
|
||||||
)
|
)
|
||||||
}.let { workgroup ->
|
}.let { workgroup ->
|
||||||
voter.assert { canCreate(workgroup, citizenOrNull) }
|
ac.assert { canCreate(workgroup, citizenOrNull) }
|
||||||
repo.upsert(workgroup)
|
repo.upsert(workgroup)
|
||||||
}.let {
|
}.let {
|
||||||
call.respond(HttpStatusCode.Created, it)
|
call.respond(HttpStatusCode.Created, it)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package fr.dcproject.component.workgroup.routes
|
package fr.dcproject.component.workgroup.routes
|
||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.WorkgroupRepository
|
import fr.dcproject.component.workgroup.WorkgroupRepository
|
||||||
import fr.dcproject.component.workgroup.WorkgroupVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -18,10 +18,10 @@ object DeleteWorkgroup {
|
|||||||
@Location("/workgroups/{workgroupId}")
|
@Location("/workgroups/{workgroupId}")
|
||||||
class DeleteWorkgroupRequest(val workgroupId: UUID)
|
class DeleteWorkgroupRequest(val workgroupId: UUID)
|
||||||
|
|
||||||
fun Route.deleteWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
|
fun Route.deleteWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
delete<DeleteWorkgroupRequest> {
|
delete<DeleteWorkgroupRequest> {
|
||||||
repo.findById(it.workgroupId)?.let { workgroup ->
|
repo.findById(it.workgroupId)?.let { workgroup ->
|
||||||
voter.assert { canDelete(workgroup, citizenOrNull) }
|
ac.assert { canDelete(workgroup, citizenOrNull) }
|
||||||
repo.delete(workgroup)
|
repo.delete(workgroup)
|
||||||
call.respond(HttpStatusCode.NoContent)
|
call.respond(HttpStatusCode.NoContent)
|
||||||
} ?: call.respond(HttpStatusCode.NotFound)
|
} ?: call.respond(HttpStatusCode.NotFound)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package fr.dcproject.component.workgroup.routes
|
package fr.dcproject.component.workgroup.routes
|
||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.WorkgroupRepository
|
import fr.dcproject.component.workgroup.WorkgroupRepository
|
||||||
import fr.dcproject.component.workgroup.WorkgroupVoter
|
|
||||||
import fr.dcproject.component.workgroup.routes.EditWorkgroup.PutWorkgroupRequest.Input
|
import fr.dcproject.component.workgroup.routes.EditWorkgroup.PutWorkgroupRequest.Input
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -28,7 +28,7 @@ object EditWorkgroup {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.editWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
|
fun Route.editWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
put<PutWorkgroupRequest> {
|
put<PutWorkgroupRequest> {
|
||||||
repo.findById(it.workgroupId)?.let { old ->
|
repo.findById(it.workgroupId)?.let { old ->
|
||||||
call.receive<Input>().run {
|
call.receive<Input>().run {
|
||||||
@@ -38,7 +38,7 @@ object EditWorkgroup {
|
|||||||
logo = logo ?: old.logo,
|
logo = logo ?: old.logo,
|
||||||
anonymous = anonymous ?: old.anonymous
|
anonymous = anonymous ?: old.anonymous
|
||||||
).let { workgroup ->
|
).let { workgroup ->
|
||||||
voter.assert { canUpdate(workgroup, citizenOrNull) }
|
ac.assert { canUpdate(workgroup, citizenOrNull) }
|
||||||
repo.upsert(workgroup)
|
repo.upsert(workgroup)
|
||||||
call.respond(HttpStatusCode.OK, it)
|
call.respond(HttpStatusCode.OK, it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package fr.dcproject.component.workgroup.routes
|
package fr.dcproject.component.workgroup.routes
|
||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.WorkgroupRepository
|
import fr.dcproject.component.workgroup.WorkgroupRepository
|
||||||
import fr.dcproject.component.workgroup.WorkgroupVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -18,10 +18,10 @@ object GetWorkgroup {
|
|||||||
@Location("/workgroups/{workgroupId}")
|
@Location("/workgroups/{workgroupId}")
|
||||||
class WorkgroupRequest(val workgroupId: UUID)
|
class WorkgroupRequest(val workgroupId: UUID)
|
||||||
|
|
||||||
fun Route.getWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
|
fun Route.getWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
get<WorkgroupRequest> {
|
get<WorkgroupRequest> {
|
||||||
repo.findById(it.workgroupId)?.let { workgroup ->
|
repo.findById(it.workgroupId)?.let { workgroup ->
|
||||||
voter.assert { canView(workgroup, citizenOrNull) }
|
ac.assert { canView(workgroup, citizenOrNull) }
|
||||||
call.respond(workgroup)
|
call.respond(workgroup)
|
||||||
} ?: call.respond(HttpStatusCode.NotFound)
|
} ?: call.respond(HttpStatusCode.NotFound)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package fr.dcproject.component.workgroup.routes
|
package fr.dcproject.component.workgroup.routes
|
||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.WorkgroupRepository
|
import fr.dcproject.component.workgroup.WorkgroupRepository
|
||||||
import fr.dcproject.component.workgroup.WorkgroupVoter
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.utils.toUUID
|
import fr.dcproject.utils.toUUID
|
||||||
import fr.dcproject.voter.assert
|
|
||||||
import fr.postgresjson.repository.RepositoryI
|
import fr.postgresjson.repository.RepositoryI
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -31,7 +31,7 @@ object GetWorkgroups {
|
|||||||
val members: List<UUID>? = members?.toUUID()
|
val members: List<UUID>? = members?.toUUID()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.getWorkgroups(repo: WorkgroupRepository, voter: WorkgroupVoter) {
|
fun Route.getWorkgroups(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
get<WorkgroupsRequest> {
|
get<WorkgroupsRequest> {
|
||||||
val workgroups =
|
val workgroups =
|
||||||
repo.find(
|
repo.find(
|
||||||
@@ -42,7 +42,7 @@ object GetWorkgroups {
|
|||||||
it.search,
|
it.search,
|
||||||
WorkgroupRepository.Filter(createdById = it.createdBy, members = it.members)
|
WorkgroupRepository.Filter(createdById = it.createdBy, members = it.members)
|
||||||
)
|
)
|
||||||
voter.assert { canView(workgroups.result, citizenOrNull) }
|
ac.assert { canView(workgroups.result, citizenOrNull) }
|
||||||
call.respond(workgroups)
|
call.respond(workgroups)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ package fr.dcproject.component.workgroup.routes.members
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.CitizenRef
|
import fr.dcproject.component.citizen.CitizenRef
|
||||||
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.WorkgroupRepository
|
import fr.dcproject.component.workgroup.WorkgroupRepository
|
||||||
import fr.dcproject.component.workgroup.WorkgroupVoter
|
|
||||||
import fr.dcproject.component.workgroup.WorkgroupWithMembersI
|
import fr.dcproject.component.workgroup.WorkgroupWithMembersI
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.ApplicationCall
|
import io.ktor.application.ApplicationCall
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
@@ -40,12 +40,12 @@ object AddMemberToWorkgroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@KtorExperimentalLocationsAPI
|
@KtorExperimentalLocationsAPI
|
||||||
fun Route.addMemberToWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
|
fun Route.addMemberToWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
/* Add members to workgroup */
|
/* Add members to workgroup */
|
||||||
post<WorkgroupsMembersRequest> {
|
post<WorkgroupsMembersRequest> {
|
||||||
repo.findById(it.workgroupId)?.let { workgroup ->
|
repo.findById(it.workgroupId)?.let { workgroup ->
|
||||||
call.getMembersFromRequest().let { members ->
|
call.getMembersFromRequest().let { members ->
|
||||||
voter.assert { canAddMembers(workgroup, citizenOrNull) }
|
ac.assert { canAddMembers(workgroup, citizenOrNull) }
|
||||||
repo.addMembers(workgroup, members)
|
repo.addMembers(workgroup, members)
|
||||||
}.let { members ->
|
}.let { members ->
|
||||||
call.respond(HttpStatusCode.Created, members)
|
call.respond(HttpStatusCode.Created, members)
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ package fr.dcproject.component.workgroup.routes.members
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.CitizenRef
|
import fr.dcproject.component.citizen.CitizenRef
|
||||||
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.WorkgroupRepository
|
import fr.dcproject.component.workgroup.WorkgroupRepository
|
||||||
import fr.dcproject.component.workgroup.WorkgroupVoter
|
|
||||||
import fr.dcproject.component.workgroup.WorkgroupWithMembersI
|
import fr.dcproject.component.workgroup.WorkgroupWithMembersI
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.ApplicationCall
|
import io.ktor.application.ApplicationCall
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
@@ -38,12 +38,12 @@ object DeleteMembersOfWorkgroup {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.deleteMemberOfWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
|
fun Route.deleteMemberOfWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
/* Delete members of workgroup */
|
/* Delete members of workgroup */
|
||||||
delete<WorkgroupsMembersRequest> {
|
delete<WorkgroupsMembersRequest> {
|
||||||
repo.findById(it.workgroupId)?.let { workgroup ->
|
repo.findById(it.workgroupId)?.let { workgroup ->
|
||||||
call.getMembersFromRequest().let { members ->
|
call.getMembersFromRequest().let { members ->
|
||||||
voter.assert { canView(workgroup, citizenOrNull) }
|
ac.assert { canView(workgroup, citizenOrNull) }
|
||||||
repo.removeMembers(workgroup, members)
|
repo.removeMembers(workgroup, members)
|
||||||
}.let { members ->
|
}.let { members ->
|
||||||
call.respond(HttpStatusCode.OK, members)
|
call.respond(HttpStatusCode.OK, members)
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ package fr.dcproject.component.workgroup.routes.members
|
|||||||
|
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.CitizenRef
|
import fr.dcproject.component.citizen.CitizenRef
|
||||||
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.WorkgroupRepository
|
import fr.dcproject.component.workgroup.WorkgroupRepository
|
||||||
import fr.dcproject.component.workgroup.WorkgroupVoter
|
|
||||||
import fr.dcproject.component.workgroup.WorkgroupWithMembersI
|
import fr.dcproject.component.workgroup.WorkgroupWithMembersI
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.ApplicationCall
|
import io.ktor.application.ApplicationCall
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
@@ -38,12 +38,12 @@ object UpdateMemberOfWorkgroup {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.updateMemberOfWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
|
fun Route.updateMemberOfWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
/* Update members of workgroup */
|
/* Update members of workgroup */
|
||||||
put<WorkgroupsMembersRequest> {
|
put<WorkgroupsMembersRequest> {
|
||||||
repo.findById(it.workgroupId)?.let { workgroup ->
|
repo.findById(it.workgroupId)?.let { workgroup ->
|
||||||
call.getMembersFromRequest().let { members ->
|
call.getMembersFromRequest().let { members ->
|
||||||
voter.assert { canUpdateMembers(workgroup, citizenOrNull) }
|
ac.assert { canUpdateMembers(workgroup, citizenOrNull) }
|
||||||
repo.updateMembers(workgroup, members)
|
repo.updateMembers(workgroup, members)
|
||||||
}.let { members ->
|
}.let { members ->
|
||||||
call.respond(HttpStatusCode.OK, members)
|
call.respond(HttpStatusCode.OK, members)
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ package fr.dcproject.routes
|
|||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.citizen.Citizen
|
import fr.dcproject.component.citizen.Citizen
|
||||||
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
import fr.dcproject.component.comment.generic.CommentForUpdate
|
import fr.dcproject.component.comment.generic.CommentForUpdate
|
||||||
import fr.dcproject.component.comment.generic.CommentVoter
|
|
||||||
import fr.dcproject.entity.ConstitutionRef
|
import fr.dcproject.entity.ConstitutionRef
|
||||||
import fr.dcproject.repository.CommentConstitutionRepository
|
import fr.dcproject.repository.CommentConstitutionRepository
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.assert
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -28,10 +28,10 @@ object CommentConstitutionPaths {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@KtorExperimentalLocationsAPI
|
@KtorExperimentalLocationsAPI
|
||||||
fun Route.commentConstitution(repo: CommentConstitutionRepository, voter: CommentVoter) {
|
fun Route.commentConstitution(repo: CommentConstitutionRepository, ac: CommentAccessControl) {
|
||||||
get<CommentConstitutionPaths.ConstitutionCommentRequest> {
|
get<CommentConstitutionPaths.ConstitutionCommentRequest> {
|
||||||
val comments = repo.findByTarget(it.constitution)
|
val comments = repo.findByTarget(it.constitution)
|
||||||
voter.assert { canView(comments.result, citizenOrNull) }
|
ac.assert { canView(comments.result, citizenOrNull) }
|
||||||
call.respond(HttpStatusCode.OK, comments)
|
call.respond(HttpStatusCode.OK, comments)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ fun Route.commentConstitution(repo: CommentConstitutionRepository, voter: Commen
|
|||||||
createdBy = citizen,
|
createdBy = citizen,
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
voter.assert { canCreate(comment, citizenOrNull) }
|
ac.assert { canCreate(comment, citizenOrNull) }
|
||||||
repo.comment(comment)
|
repo.comment(comment)
|
||||||
|
|
||||||
call.respond(HttpStatusCode.Created, comment)
|
call.respond(HttpStatusCode.Created, comment)
|
||||||
@@ -50,7 +50,7 @@ fun Route.commentConstitution(repo: CommentConstitutionRepository, voter: Commen
|
|||||||
|
|
||||||
get<CommentConstitutionPaths.CitizenCommentConstitutionRequest> {
|
get<CommentConstitutionPaths.CitizenCommentConstitutionRequest> {
|
||||||
val comments = repo.findByCitizen(it.citizen)
|
val comments = repo.findByCitizen(it.citizen)
|
||||||
voter.assert { canView(comments.result, citizenOrNull) }
|
ac.assert { canView(comments.result, citizenOrNull) }
|
||||||
call.respond(comments)
|
call.respond(comments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ 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
|
import fr.dcproject.security.assert
|
||||||
import fr.dcproject.voter.assert
|
import fr.dcproject.security.voter.ConstitutionAccessControl
|
||||||
import fr.postgresjson.entity.UuidEntity
|
import fr.postgresjson.entity.UuidEntity
|
||||||
import fr.postgresjson.repository.RepositoryI
|
import fr.postgresjson.repository.RepositoryI
|
||||||
import io.ktor.application.ApplicationCall
|
import io.ktor.application.ApplicationCall
|
||||||
@@ -88,21 +88,21 @@ object ConstitutionPaths {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@KtorExperimentalLocationsAPI
|
@KtorExperimentalLocationsAPI
|
||||||
fun Route.constitution(repo: ConstitutionRepository, voter: ConstitutionVoter) {
|
fun Route.constitution(repo: ConstitutionRepository, ac: ConstitutionAccessControl) {
|
||||||
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)
|
||||||
voter.assert { canView(constitutions.result, citizenOrNull) }
|
ac.assert { canView(constitutions.result, citizenOrNull) }
|
||||||
call.respond(constitutions)
|
call.respond(constitutions)
|
||||||
}
|
}
|
||||||
|
|
||||||
get<ConstitutionPaths.ConstitutionRequest> {
|
get<ConstitutionPaths.ConstitutionRequest> {
|
||||||
voter.assert { canView(it.constitution, citizenOrNull) }
|
ac.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 ->
|
||||||
voter.assert { canCreate(constitution, citizenOrNull) }
|
ac.assert { canCreate(constitution, citizenOrNull) }
|
||||||
repo.upsert(constitution)
|
repo.upsert(constitution)
|
||||||
call.respond(constitution)
|
call.respond(constitution)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package fr.dcproject.voter
|
package fr.dcproject.security
|
||||||
|
|
||||||
/** Responses of voters */
|
/** Responses of AccessControl */
|
||||||
enum class Vote {
|
enum class AccessDecision {
|
||||||
GRANTED,
|
GRANTED,
|
||||||
DENIED;
|
DENIED;
|
||||||
|
|
||||||
/** Helper to convert true/false to GRANTED/DENIED */
|
/** Helper to convert true/false to GRANTED/DENIED */
|
||||||
companion object {
|
companion object {
|
||||||
fun toVote(lambda: () -> Boolean): Vote = when (lambda()) {
|
fun toVote(lambda: () -> Boolean): AccessDecision = when (lambda()) {
|
||||||
true -> GRANTED
|
true -> GRANTED
|
||||||
false -> DENIED
|
false -> DENIED
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ enum class Vote {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class Voter {
|
abstract class AccessControl {
|
||||||
/**
|
/**
|
||||||
* A Shortcut for return a GrantedResponse
|
* A Shortcut for return a GrantedResponse
|
||||||
*/
|
*/
|
||||||
@@ -37,20 +37,20 @@ abstract class Voter {
|
|||||||
*
|
*
|
||||||
* If the list of responses is empty, return GRANTED
|
* If the list of responses is empty, return GRANTED
|
||||||
*/
|
*/
|
||||||
private fun VoterResponses.getOneResponse(): VoterResponse = this.firstOrNull { it.vote == Vote.DENIED } ?: granted()
|
private fun AccessResponses.getOneResponse(): AccessResponse = this.firstOrNull { it.decision == AccessDecision.DENIED } ?: granted()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An helper to convert a list of subject into one response
|
* An helper to convert a list of subject into one response
|
||||||
*/
|
*/
|
||||||
protected fun <S : List<T>, T> canAll(items: S, action: (T) -> VoterResponse): VoterResponse = items
|
protected fun <S : List<T>, T> canAll(items: S, action: (T) -> AccessResponse): AccessResponse = items
|
||||||
.map { action(it) }
|
.map { action(it) }
|
||||||
.getOneResponse()
|
.getOneResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throw an Exception if voter return a DENIED response
|
* Throw an Exception if AccessControl return a DENIED response
|
||||||
*/
|
*/
|
||||||
fun <T : Voter> T.assert(action: T.() -> VoterResponse) {
|
fun <T : AccessControl> T.assert(action: T.() -> AccessResponse) {
|
||||||
action().assert()
|
action().assert()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,84 +59,84 @@ fun <T : Voter> T.assert(action: T.() -> VoterResponse) {
|
|||||||
*
|
*
|
||||||
* If the list of responses is empty, return GRANTED
|
* If the list of responses is empty, return GRANTED
|
||||||
*/
|
*/
|
||||||
fun VoterResponses.getOneResponse(): VoterResponse = this.firstOrNull { it.vote == Vote.DENIED } ?: GrantedResponse(first().voter)
|
fun AccessResponses.getOneResponse(): AccessResponse = this.firstOrNull { it.decision == AccessDecision.DENIED } ?: GrantedResponse(first().accessControl)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throw an Exception if one response is DENIED
|
* Throw an Exception if one response is DENIED
|
||||||
*/
|
*/
|
||||||
fun VoterResponses.assert() = this.getOneResponse().assert()
|
fun AccessResponses.assert() = this.getOneResponse().assert()
|
||||||
|
|
||||||
class VoterDeniedException(private val voterResponses: VoterResponses) : Throwable(voterResponses.first().message) {
|
class AccessDeniedException(private val accessResponses: AccessResponses) : Throwable(accessResponses.first().message) {
|
||||||
constructor(voterResponse: VoterResponse) : this(listOf(voterResponse))
|
constructor(accessResponse: AccessResponse) : this(listOf(accessResponse))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get first response
|
* Get first response
|
||||||
*/
|
*/
|
||||||
fun first(): VoterResponse = voterResponses.first()
|
fun first(): AccessResponse = accessResponses.first()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the error code is present into the responses
|
* Check if the error code is present into the responses
|
||||||
*/
|
*/
|
||||||
fun hasErrorCode(code: String): Boolean = voterResponses
|
fun hasErrorCode(code: String): Boolean = accessResponses
|
||||||
.filter { it.vote == Vote.DENIED }
|
.filter { it.decision == AccessDecision.DENIED }
|
||||||
.any { it.code == code }
|
.any { it.code == code }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find and return the response than match with the error code
|
* Find and return the response than match with the error code
|
||||||
*/
|
*/
|
||||||
fun getErrorCode(code: String): VoterResponse? = voterResponses
|
fun getErrorCode(code: String): AccessResponse? = accessResponses
|
||||||
.firstOrNull { it.vote == Vote.DENIED && it.code == code }
|
.firstOrNull { it.decision == AccessDecision.DENIED && it.code == code }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of messages of all responses
|
* Get a list of messages of all responses
|
||||||
*/
|
*/
|
||||||
fun getMessages(): List<String> = voterResponses
|
fun getMessages(): List<String> = accessResponses
|
||||||
.mapNotNull { it.message }
|
.mapNotNull { it.message }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the first message
|
* Get the first message
|
||||||
*/
|
*/
|
||||||
fun getFirstMessage(): String? = voterResponses
|
fun getFirstMessage(): String? = accessResponses
|
||||||
.first()
|
.first()
|
||||||
.message
|
.message
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The response that all Voter method return
|
* The response that all AccessControl method return
|
||||||
* @see GrantedResponse
|
* @see GrantedResponse
|
||||||
* @see DeniedResponse
|
* @see DeniedResponse
|
||||||
*/
|
*/
|
||||||
sealed class VoterResponse(
|
sealed class AccessResponse(
|
||||||
val vote: Vote,
|
val decision: AccessDecision,
|
||||||
val voter: Voter,
|
val accessControl: AccessControl,
|
||||||
val message: String?,
|
val message: String?,
|
||||||
val code: String?
|
val code: String?
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Convert response as boolean
|
* Convert response as boolean
|
||||||
*/
|
*/
|
||||||
fun toBoolean(): Boolean = vote.toBoolean()
|
fun toBoolean(): Boolean = decision.toBoolean()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throw Exception if response if DENIED
|
* Throw Exception if response if DENIED
|
||||||
*/
|
*/
|
||||||
fun assert() {
|
fun assert() {
|
||||||
if (this.vote == Vote.DENIED) {
|
if (this.decision == AccessDecision.DENIED) {
|
||||||
throw VoterDeniedException(this)
|
throw AccessDeniedException(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GrantedResponse(
|
class GrantedResponse(
|
||||||
voter: Voter,
|
accessControl: AccessControl,
|
||||||
message: String? = null,
|
message: String? = null,
|
||||||
code: String? = null
|
code: String? = null
|
||||||
) : VoterResponse(Vote.GRANTED, voter, message, code)
|
) : AccessResponse(AccessDecision.GRANTED, accessControl, message, code)
|
||||||
|
|
||||||
class DeniedResponse(
|
class DeniedResponse(
|
||||||
voter: Voter,
|
accessControl: AccessControl,
|
||||||
message: String,
|
message: String,
|
||||||
code: String
|
code: String
|
||||||
) : VoterResponse(Vote.DENIED, voter, message, code)
|
) : AccessResponse(AccessDecision.DENIED, accessControl, message, code)
|
||||||
|
|
||||||
typealias VoterResponses = List<VoterResponse>
|
typealias AccessResponses = List<AccessResponse>
|
||||||
@@ -3,30 +3,30 @@ package fr.dcproject.security.voter
|
|||||||
import fr.dcproject.component.citizen.CitizenI
|
import fr.dcproject.component.citizen.CitizenI
|
||||||
import fr.dcproject.entity.ConstitutionS
|
import fr.dcproject.entity.ConstitutionS
|
||||||
import fr.dcproject.entity.ConstitutionSimple
|
import fr.dcproject.entity.ConstitutionSimple
|
||||||
import fr.dcproject.voter.Voter
|
import fr.dcproject.security.AccessControl
|
||||||
import fr.dcproject.voter.VoterResponse
|
import fr.dcproject.security.AccessResponse
|
||||||
|
|
||||||
class ConstitutionVoter : Voter() {
|
class ConstitutionAccessControl : AccessControl() {
|
||||||
fun canCreate(subject: ConstitutionS, citizen: CitizenI?): VoterResponse = when {
|
fun canCreate(subject: ConstitutionS, citizen: CitizenI?): AccessResponse = when {
|
||||||
citizen == null -> denied("You must be connected to create constitution", "constitution.create.notConnected")
|
citizen == null -> denied("You must be connected to create constitution", "constitution.create.notConnected")
|
||||||
else -> granted()
|
else -> granted()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <S : ConstitutionSimple<*, *>> canView(subjects: List<S>, citizen: CitizenI?): VoterResponse =
|
fun <S : ConstitutionSimple<*, *>> canView(subjects: List<S>, citizen: CitizenI?): AccessResponse =
|
||||||
canAll(subjects) { canView(it, citizen) }
|
canAll(subjects) { canView(it, citizen) }
|
||||||
|
|
||||||
fun canView(subject: ConstitutionSimple<*, *>, citizen: CitizenI?): VoterResponse = when {
|
fun canView(subject: ConstitutionSimple<*, *>, citizen: CitizenI?): AccessResponse = when {
|
||||||
subject.isDeleted() -> denied("You cannot view a deleted constitution", "constitution.view.deleted")
|
subject.isDeleted() -> denied("You cannot view a deleted constitution", "constitution.view.deleted")
|
||||||
else -> granted()
|
else -> granted()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canDelete(subject: ConstitutionSimple<*, *>, citizen: CitizenI?): VoterResponse = when {
|
fun canDelete(subject: ConstitutionSimple<*, *>, citizen: CitizenI?): AccessResponse = when {
|
||||||
citizen == null -> denied("You must be connected to delete constitution", "constitution.delete.notConnected")
|
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")
|
subject.createdBy.id != citizen.id -> denied("You cannot delete the constitution of other citizen", "constitution.delete.otherCitizen")
|
||||||
else -> granted()
|
else -> granted()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canUpdate(subject: ConstitutionSimple<*, *>, citizen: CitizenI?): VoterResponse = when {
|
fun canUpdate(subject: ConstitutionSimple<*, *>, citizen: CitizenI?): AccessResponse = when {
|
||||||
citizen == null -> denied("You must be connected to update constitution", "constitution.update.notConnected")
|
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")
|
subject.createdBy.id != citizen.id -> denied("You cannot update the constitution of other citizen", "constitution.update.otherCitizen")
|
||||||
else -> granted()
|
else -> granted()
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
package unit.voter
|
package unit.security
|
||||||
|
|
||||||
|
import fr.dcproject.component.article.ArticleAccessControl
|
||||||
import fr.dcproject.component.article.ArticleForView
|
import fr.dcproject.component.article.ArticleForView
|
||||||
import fr.dcproject.component.article.ArticleVoter
|
|
||||||
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.citizen.CitizenCart
|
import fr.dcproject.component.citizen.CitizenCart
|
||||||
import fr.dcproject.component.citizen.CitizenI
|
import fr.dcproject.component.citizen.CitizenI
|
||||||
import fr.dcproject.voter.Vote.DENIED
|
import fr.dcproject.security.AccessDecision.DENIED
|
||||||
import fr.dcproject.voter.Vote.GRANTED
|
import fr.dcproject.security.AccessDecision.GRANTED
|
||||||
import fr.postgresjson.connexion.Paginated
|
import fr.postgresjson.connexion.Paginated
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
@@ -23,8 +23,8 @@ import fr.dcproject.component.article.ArticleRepository as ArticleRepo
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tag("voter")
|
@Tag("security")
|
||||||
internal class ArticleVoterTest {
|
internal class ArticleAccessControlTest {
|
||||||
private val tesla = CitizenCart(
|
private val tesla = CitizenCart(
|
||||||
id = UUID.fromString("e6efc288-4283-4729-a268-6debb18de1a0"),
|
id = UUID.fromString("e6efc288-4283-4729-a268-6debb18de1a0"),
|
||||||
user = User(
|
user = User(
|
||||||
@@ -50,35 +50,35 @@ internal class ArticleVoterTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `creator can be view the article`() {
|
fun `creator can be view the article`() {
|
||||||
val article = getArticle(tesla).copy(draft = true)
|
val article = getArticle(tesla).copy(draft = true)
|
||||||
ArticleVoter(getRepo(article))
|
ArticleAccessControl(getRepo(article))
|
||||||
.canView(article, tesla)
|
.canView(article, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `other user can be view the article`() {
|
fun `other user can be view the article`() {
|
||||||
val article = getArticle(tesla)
|
val article = getArticle(tesla)
|
||||||
ArticleVoter(getRepo(article))
|
ArticleAccessControl(getRepo(article))
|
||||||
.canView(article, einstein)
|
.canView(article, einstein)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `other user can be view the article list`(): Unit = listOf(ArticleVoter(mockk())).run {
|
fun `other user can be view the article list`(): Unit = listOf(ArticleAccessControl(mockk())).run {
|
||||||
val article = getArticle(tesla)
|
val article = getArticle(tesla)
|
||||||
val article2 = getArticle(tesla)
|
val article2 = getArticle(tesla)
|
||||||
|
|
||||||
ArticleVoter(getRepo(article))
|
ArticleAccessControl(getRepo(article))
|
||||||
.canView(listOf(article, article2), einstein)
|
.canView(listOf(article, article2), einstein)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `the no creator can not be view the article on draft`() {
|
fun `the no creator can not be view the article on draft`() {
|
||||||
val article = getArticle(tesla).copy(draft = true)
|
val article = getArticle(tesla).copy(draft = true)
|
||||||
ArticleVoter(getRepo(article))
|
ArticleAccessControl(getRepo(article))
|
||||||
.canView(article, einstein)
|
.canView(article, einstein)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -86,31 +86,31 @@ internal class ArticleVoterTest {
|
|||||||
val article = getArticle(tesla)
|
val article = getArticle(tesla)
|
||||||
val article2 = getArticle(tesla).copy(draft = true)
|
val article2 = getArticle(tesla).copy(draft = true)
|
||||||
|
|
||||||
ArticleVoter(getRepo(article))
|
ArticleAccessControl(getRepo(article))
|
||||||
.canView(listOf(article, article2), einstein)
|
.canView(listOf(article, article2), einstein)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not view deleted article`() {
|
fun `can not view deleted article`() {
|
||||||
val article = getArticle(tesla).copy(deletedAt = DateTime.now())
|
val article = getArticle(tesla).copy(deletedAt = DateTime.now())
|
||||||
ArticleVoter(getRepo(article))
|
ArticleAccessControl(getRepo(article))
|
||||||
.canView(article, tesla)
|
.canView(article, tesla)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can delete article if owner`() {
|
fun `can delete article if owner`() {
|
||||||
val article = getArticle(tesla)
|
val article = getArticle(tesla)
|
||||||
ArticleVoter(getRepo(article))
|
ArticleAccessControl(getRepo(article))
|
||||||
.canDelete(article, tesla)
|
.canDelete(article, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not delete article if not owner`() {
|
fun `can not delete article if not owner`() {
|
||||||
val article = getArticle(tesla).copy(deletedAt = DateTime.now())
|
val article = getArticle(tesla).copy(deletedAt = DateTime.now())
|
||||||
ArticleVoter(getRepo(article))
|
ArticleAccessControl(getRepo(article))
|
||||||
.canDelete(article, einstein)
|
.canDelete(article, einstein)
|
||||||
.code `should be` "article.delete.notYours"
|
.code `should be` "article.delete.notYours"
|
||||||
}
|
}
|
||||||
@@ -118,15 +118,15 @@ internal class ArticleVoterTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `can create article if logged`() {
|
fun `can create article if logged`() {
|
||||||
val article = getArticle(tesla)
|
val article = getArticle(tesla)
|
||||||
ArticleVoter(getRepo(article))
|
ArticleAccessControl(getRepo(article))
|
||||||
.canUpsert(article, tesla)
|
.canUpsert(article, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not create article if not logged`() {
|
fun `can not create article if not logged`() {
|
||||||
val article = getArticle(tesla)
|
val article = getArticle(tesla)
|
||||||
ArticleVoter(getRepo(article))
|
ArticleAccessControl(getRepo(article))
|
||||||
.canUpsert(article, null)
|
.canUpsert(article, null)
|
||||||
.code `should be` "article.create.notConnected"
|
.code `should be` "article.create.notConnected"
|
||||||
}
|
}
|
||||||
@@ -134,15 +134,15 @@ internal class ArticleVoterTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `can update article if yours`() {
|
fun `can update article if yours`() {
|
||||||
val article = getArticle(tesla)
|
val article = getArticle(tesla)
|
||||||
ArticleVoter(getRepo(article))
|
ArticleAccessControl(getRepo(article))
|
||||||
.canUpsert(article, tesla)
|
.canUpsert(article, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not update article if not yours`() {
|
fun `can not update article if not yours`() {
|
||||||
val article = getArticle(tesla)
|
val article = getArticle(tesla)
|
||||||
ArticleVoter(getRepo(article))
|
ArticleAccessControl(getRepo(article))
|
||||||
.canUpsert(article, einstein)
|
.canUpsert(article, einstein)
|
||||||
.code `should be` "article.update.notYours"
|
.code `should be` "article.update.notYours"
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package unit.voter
|
package unit.security
|
||||||
|
|
||||||
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.citizen.CitizenAccessControl
|
||||||
import fr.dcproject.component.citizen.CitizenBasic
|
import fr.dcproject.component.citizen.CitizenBasic
|
||||||
import fr.dcproject.component.citizen.CitizenI
|
import fr.dcproject.component.citizen.CitizenI
|
||||||
import fr.dcproject.component.citizen.CitizenVoter
|
import fr.dcproject.security.AccessDecision.DENIED
|
||||||
import fr.dcproject.voter.Vote.DENIED
|
import fr.dcproject.security.AccessDecision.GRANTED
|
||||||
import fr.dcproject.voter.Vote.GRANTED
|
|
||||||
import org.amshove.kluent.`should be`
|
import org.amshove.kluent.`should be`
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
@@ -17,8 +17,8 @@ import org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tag("voter")
|
@Tag("security")
|
||||||
internal class CitizenVoterTest {
|
internal class CitizenAccessControlTest {
|
||||||
private val tesla = CitizenBasic(
|
private val tesla = CitizenBasic(
|
||||||
user = User(
|
user = User(
|
||||||
username = "nicolas-tesla",
|
username = "nicolas-tesla",
|
||||||
@@ -51,50 +51,50 @@ internal class CitizenVoterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view the citizen`() {
|
fun `can be view the citizen`() {
|
||||||
CitizenVoter()
|
CitizenAccessControl()
|
||||||
.canView(subject = einstein, connectedCitizen = tesla)
|
.canView(subject = einstein, connectedCitizen = tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view the citizen list`() {
|
fun `can be view the citizen list`() {
|
||||||
CitizenVoter()
|
CitizenAccessControl()
|
||||||
.canView(subjects = listOf(tesla, einstein), connectedCitizen = einstein)
|
.canView(subjects = listOf(tesla, einstein), connectedCitizen = einstein)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not view deleted citizen`() {
|
fun `can not view deleted citizen`() {
|
||||||
CitizenVoter()
|
CitizenAccessControl()
|
||||||
.canView(subject = curie, connectedCitizen = tesla)
|
.canView(subject = curie, connectedCitizen = tesla)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be update itself`() {
|
fun `can be update itself`() {
|
||||||
CitizenVoter()
|
CitizenAccessControl()
|
||||||
.canUpdate(subject = einstein, connectedCitizen = einstein)
|
.canUpdate(subject = einstein, connectedCitizen = einstein)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be update other citizen`() {
|
fun `can not be update other citizen`() {
|
||||||
CitizenVoter()
|
CitizenAccessControl()
|
||||||
.canUpdate(subject = tesla, connectedCitizen = einstein)
|
.canUpdate(subject = tesla, connectedCitizen = einstein)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be change password of itself`() {
|
fun `can be change password of itself`() {
|
||||||
CitizenVoter()
|
CitizenAccessControl()
|
||||||
.canChangePassword(subject = einstein, connectedCitizen = einstein)
|
.canChangePassword(subject = einstein, connectedCitizen = einstein)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be change password of other citizen`() {
|
fun `can not be change password of other citizen`() {
|
||||||
CitizenVoter()
|
CitizenAccessControl()
|
||||||
.canChangePassword(subject = tesla, connectedCitizen = einstein)
|
.canChangePassword(subject = tesla, connectedCitizen = einstein)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package unit.voter
|
package unit.security
|
||||||
|
|
||||||
import fr.dcproject.component.article.ArticleForView
|
import fr.dcproject.component.article.ArticleForView
|
||||||
import fr.dcproject.component.article.ArticleRef
|
import fr.dcproject.component.article.ArticleRef
|
||||||
@@ -7,11 +7,11 @@ import fr.dcproject.component.auth.UserI
|
|||||||
import fr.dcproject.component.citizen.Citizen
|
import fr.dcproject.component.citizen.Citizen
|
||||||
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.component.comment.generic.CommentAccessControl
|
||||||
import fr.dcproject.component.comment.generic.CommentForUpdate
|
import fr.dcproject.component.comment.generic.CommentForUpdate
|
||||||
import fr.dcproject.component.comment.generic.CommentForView
|
import fr.dcproject.component.comment.generic.CommentForView
|
||||||
import fr.dcproject.component.comment.generic.CommentVoter
|
import fr.dcproject.security.AccessDecision.DENIED
|
||||||
import fr.dcproject.voter.Vote.DENIED
|
import fr.dcproject.security.AccessDecision.GRANTED
|
||||||
import fr.dcproject.voter.Vote.GRANTED
|
|
||||||
import org.amshove.kluent.`should be`
|
import org.amshove.kluent.`should be`
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
@@ -23,8 +23,8 @@ import java.util.UUID
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tag("voter")
|
@Tag("security")
|
||||||
internal class CommentVoterTest {
|
internal class CommentAccessControlTest {
|
||||||
private val tesla = Citizen(
|
private val tesla = Citizen(
|
||||||
user = User(
|
user = User(
|
||||||
username = "nicolas-tesla",
|
username = "nicolas-tesla",
|
||||||
@@ -99,57 +99,57 @@ internal class CommentVoterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view the comment`() {
|
fun `can be view the comment`() {
|
||||||
CommentVoter()
|
CommentAccessControl()
|
||||||
.canView(comment1, tesla)
|
.canView(comment1, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view the comment list`() {
|
fun `can be view the comment list`() {
|
||||||
CommentVoter()
|
CommentAccessControl()
|
||||||
.canView(listOf(comment1, comment2), einstein)
|
.canView(listOf(comment1, comment2), einstein)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be update your comment`() {
|
fun `can be update your comment`() {
|
||||||
CommentVoter()
|
CommentAccessControl()
|
||||||
.canUpdate(comment1, tesla)
|
.canUpdate(comment1, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be update other comment`() {
|
fun `can not be update other comment`() {
|
||||||
CommentVoter()
|
CommentAccessControl()
|
||||||
.canUpdate(comment1, einstein)
|
.canUpdate(comment1, einstein)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be create a comment`() {
|
fun `can be create a comment`() {
|
||||||
CommentVoter()
|
CommentAccessControl()
|
||||||
.canCreate(comment1, tesla)
|
.canCreate(comment1, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be create a comment if target is deleted`() {
|
fun `can not be create a comment if target is deleted`() {
|
||||||
CommentVoter()
|
CommentAccessControl()
|
||||||
.canCreate(commentTargetDeleted, tesla)
|
.canCreate(commentTargetDeleted, tesla)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be create a comment with other creator`() {
|
fun `can not be create a comment with other creator`() {
|
||||||
CommentVoter()
|
CommentAccessControl()
|
||||||
.canCreate(comment1, einstein)
|
.canCreate(comment1, einstein)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be create a comment if not connected`() {
|
fun `can not be create a comment if not connected`() {
|
||||||
CommentVoter()
|
CommentAccessControl()
|
||||||
.canCreate(comment1, null)
|
.canCreate(comment1, null)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package unit.voter
|
package unit.security
|
||||||
|
|
||||||
import fr.dcproject.component.article.ArticleForView
|
import fr.dcproject.component.article.ArticleForView
|
||||||
import fr.dcproject.component.auth.User
|
import fr.dcproject.component.auth.User
|
||||||
@@ -8,9 +8,9 @@ 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.component.follow.Follow
|
import fr.dcproject.component.follow.Follow
|
||||||
import fr.dcproject.component.follow.FollowVoter
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.voter.Vote.DENIED
|
import fr.dcproject.security.AccessDecision.DENIED
|
||||||
import fr.dcproject.voter.Vote.GRANTED
|
import fr.dcproject.security.AccessDecision.GRANTED
|
||||||
import org.amshove.kluent.`should be`
|
import org.amshove.kluent.`should be`
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
@@ -22,8 +22,8 @@ import java.util.UUID
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tag("voter")
|
@Tag("security")
|
||||||
internal class FollowVoterTest {
|
internal class FollowAccessControlTest {
|
||||||
private val tesla = CitizenBasic(
|
private val tesla = CitizenBasic(
|
||||||
user = User(
|
user = User(
|
||||||
username = "nicolas-tesla",
|
username = "nicolas-tesla",
|
||||||
@@ -97,57 +97,57 @@ internal class FollowVoterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view the follow`() {
|
fun `can be view the follow`() {
|
||||||
FollowVoter()
|
FollowAccessControl()
|
||||||
.canView(follow1, tesla2)
|
.canView(follow1, tesla2)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view the follow list`() {
|
fun `can be view the follow list`() {
|
||||||
FollowVoter()
|
FollowAccessControl()
|
||||||
.canView(listOf(follow1), tesla2)
|
.canView(listOf(follow1), tesla2)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view your anonymous follow`() {
|
fun `can be view your anonymous follow`() {
|
||||||
FollowVoter()
|
FollowAccessControl()
|
||||||
.canView(followAnon, einstein3)
|
.canView(followAnon, einstein3)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be view the anonymous follow of other`() {
|
fun `can not be view the anonymous follow of other`() {
|
||||||
FollowVoter()
|
FollowAccessControl()
|
||||||
.canView(followAnon, tesla2)
|
.canView(followAnon, tesla2)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be follow article`() {
|
fun `can be follow article`() {
|
||||||
FollowVoter()
|
FollowAccessControl()
|
||||||
.canCreate(follow1, tesla2)
|
.canCreate(follow1, tesla2)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be follow article if not connected`() {
|
fun `can not be follow article if not connected`() {
|
||||||
FollowVoter()
|
FollowAccessControl()
|
||||||
.canCreate(follow1, null)
|
.canCreate(follow1, null)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be unfollow article`() {
|
fun `can be unfollow article`() {
|
||||||
FollowVoter()
|
FollowAccessControl()
|
||||||
.canDelete(follow1, tesla2)
|
.canDelete(follow1, tesla2)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be unfollow article if not connected`() {
|
fun `can not be unfollow article if not connected`() {
|
||||||
FollowVoter()
|
FollowAccessControl()
|
||||||
.canDelete(follow1, null)
|
.canDelete(follow1, null)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package unit.voter
|
package unit.security
|
||||||
|
|
||||||
import fr.dcproject.component.article.ArticleForView
|
import fr.dcproject.component.article.ArticleForView
|
||||||
import fr.dcproject.component.auth.User
|
import fr.dcproject.component.auth.User
|
||||||
@@ -6,11 +6,11 @@ import fr.dcproject.component.auth.UserI
|
|||||||
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.component.opinion.OpinionVoter
|
import fr.dcproject.component.opinion.OpinionAccessControl
|
||||||
import fr.dcproject.component.opinion.entity.Opinion
|
import fr.dcproject.component.opinion.entity.Opinion
|
||||||
import fr.dcproject.component.opinion.entity.OpinionChoice
|
import fr.dcproject.component.opinion.entity.OpinionChoice
|
||||||
import fr.dcproject.voter.Vote.DENIED
|
import fr.dcproject.security.AccessDecision.DENIED
|
||||||
import fr.dcproject.voter.Vote.GRANTED
|
import fr.dcproject.security.AccessDecision.GRANTED
|
||||||
import org.amshove.kluent.`should be`
|
import org.amshove.kluent.`should be`
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
@@ -22,8 +22,8 @@ import java.util.UUID
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tag("voter")
|
@Tag("security")
|
||||||
internal class OpinionVoterTest {
|
internal class OpinionAccessControlTest {
|
||||||
private val tesla = CitizenBasic(
|
private val tesla = CitizenBasic(
|
||||||
user = User(
|
user = User(
|
||||||
username = "nicolas-tesla",
|
username = "nicolas-tesla",
|
||||||
@@ -74,50 +74,50 @@ internal class OpinionVoterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view the opinion`() {
|
fun `can be view the opinion`() {
|
||||||
OpinionVoter()
|
OpinionAccessControl()
|
||||||
.canView(opinion1, tesla)
|
.canView(opinion1, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view the opinion list`() {
|
fun `can be view the opinion list`() {
|
||||||
OpinionVoter()
|
OpinionAccessControl()
|
||||||
.canView(listOf(opinion1), tesla)
|
.canView(listOf(opinion1), tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be opinion an article`() {
|
fun `can be opinion an article`() {
|
||||||
OpinionVoter()
|
OpinionAccessControl()
|
||||||
.canCreate(opinion1, tesla)
|
.canCreate(opinion1, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be opinion if not connected`() {
|
fun `can not be opinion if not connected`() {
|
||||||
OpinionVoter()
|
OpinionAccessControl()
|
||||||
.canCreate(opinion1, null)
|
.canCreate(opinion1, null)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be remove opinion`() {
|
fun `can be remove opinion`() {
|
||||||
OpinionVoter()
|
OpinionAccessControl()
|
||||||
.canDelete(opinion1, tesla)
|
.canDelete(opinion1, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be remove opinion if not connected`() {
|
fun `can not be remove opinion if not connected`() {
|
||||||
OpinionVoter()
|
OpinionAccessControl()
|
||||||
.canDelete(opinion1, null)
|
.canDelete(opinion1, null)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be remove opinion of other user`() {
|
fun `can not be remove opinion of other user`() {
|
||||||
OpinionVoter()
|
OpinionAccessControl()
|
||||||
.canDelete(opinion1, einstein)
|
.canDelete(opinion1, einstein)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package unit.voter
|
package unit.security
|
||||||
|
|
||||||
import fr.dcproject.component.article.ArticleForView
|
import fr.dcproject.component.article.ArticleForView
|
||||||
import fr.dcproject.component.auth.User
|
import fr.dcproject.component.auth.User
|
||||||
@@ -6,9 +6,9 @@ import fr.dcproject.component.auth.UserI
|
|||||||
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.component.opinion.OpinionChoiceVoter
|
import fr.dcproject.component.opinion.OpinionChoiceAccessControl
|
||||||
import fr.dcproject.component.opinion.entity.OpinionChoice
|
import fr.dcproject.component.opinion.entity.OpinionChoice
|
||||||
import fr.dcproject.voter.Vote.GRANTED
|
import fr.dcproject.security.AccessDecision.GRANTED
|
||||||
import org.amshove.kluent.`should be`
|
import org.amshove.kluent.`should be`
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
@@ -20,8 +20,8 @@ import java.util.UUID
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tag("voter")
|
@Tag("security")
|
||||||
internal class OpinionChoiceVoterTest {
|
internal class OpinionChoiceAccessControlTest {
|
||||||
private val tesla = CitizenBasic(
|
private val tesla = CitizenBasic(
|
||||||
id = UUID.fromString("e6efc288-4283-4729-a268-6debb18de1a0"),
|
id = UUID.fromString("e6efc288-4283-4729-a268-6debb18de1a0"),
|
||||||
user = User(
|
user = User(
|
||||||
@@ -57,15 +57,15 @@ internal class OpinionChoiceVoterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view the opinion choice`() {
|
fun `can be view the opinion choice`() {
|
||||||
OpinionChoiceVoter()
|
OpinionChoiceAccessControl()
|
||||||
.canView(choice1, tesla)
|
.canView(choice1, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view the opinion choice list`() {
|
fun `can be view the opinion choice list`() {
|
||||||
OpinionChoiceVoter()
|
OpinionChoiceAccessControl()
|
||||||
.canView(listOf(choice1), tesla)
|
.canView(listOf(choice1), tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package unit.voter
|
package unit.security
|
||||||
|
|
||||||
import fr.dcproject.component.article.ArticleForView
|
import fr.dcproject.component.article.ArticleForView
|
||||||
import fr.dcproject.component.auth.User
|
import fr.dcproject.component.auth.User
|
||||||
@@ -7,10 +7,10 @@ import fr.dcproject.component.citizen.Citizen
|
|||||||
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.component.vote.VoteVoter
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
import fr.dcproject.component.vote.entity.VoteForUpdate
|
import fr.dcproject.component.vote.entity.VoteForUpdate
|
||||||
import fr.dcproject.voter.Vote.DENIED
|
import fr.dcproject.security.AccessDecision.DENIED
|
||||||
import fr.dcproject.voter.Vote.GRANTED
|
import fr.dcproject.security.AccessDecision.GRANTED
|
||||||
import org.amshove.kluent.`should be`
|
import org.amshove.kluent.`should be`
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
@@ -23,8 +23,8 @@ import fr.dcproject.component.vote.entity.Vote as VoteEntity
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tag("voter")
|
@Tag("security")
|
||||||
internal class VoteVoterTest {
|
internal class VoteAccessControlTest {
|
||||||
private val tesla = Citizen(
|
private val tesla = Citizen(
|
||||||
id = UUID.fromString("a1e35c99-9d33-4fb4-9201-58d7071243bb"),
|
id = UUID.fromString("a1e35c99-9d33-4fb4-9201-58d7071243bb"),
|
||||||
user = User(
|
user = User(
|
||||||
@@ -101,43 +101,43 @@ internal class VoteVoterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view your the vote`() {
|
fun `can be view your the vote`() {
|
||||||
VoteVoter()
|
VoteAccessControl()
|
||||||
.canView(vote1, tesla)
|
.canView(vote1, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be view vote of other`() {
|
fun `can not be view vote of other`() {
|
||||||
VoteVoter()
|
VoteAccessControl()
|
||||||
.canView(vote1, einstein)
|
.canView(vote1, einstein)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view your votes list`() {
|
fun `can be view your votes list`() {
|
||||||
VoteVoter()
|
VoteAccessControl()
|
||||||
.canView(listOf(vote1), tesla)
|
.canView(listOf(vote1), tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be vote an article`() {
|
fun `can be vote an article`() {
|
||||||
VoteVoter()
|
VoteAccessControl()
|
||||||
.canCreate(voteForUpdate, tesla)
|
.canCreate(voteForUpdate, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be vote if not connected`() {
|
fun `can not be vote if not connected`() {
|
||||||
VoteVoter()
|
VoteAccessControl()
|
||||||
.canCreate(voteForUpdate, null)
|
.canCreate(voteForUpdate, null)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be vote an article if article is deleted`() {
|
fun `can not be vote an article if article is deleted`() {
|
||||||
VoteVoter()
|
VoteAccessControl()
|
||||||
.canCreate(voteOnDeleted, tesla)
|
.canCreate(voteOnDeleted, tesla)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
package unit.voter
|
package unit.security
|
||||||
|
|
||||||
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.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.component.workgroup.WorkgroupVoter
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.WorkgroupWithMembersI
|
import fr.dcproject.component.workgroup.WorkgroupWithMembersI
|
||||||
import fr.dcproject.voter.Vote.DENIED
|
import fr.dcproject.security.AccessDecision.DENIED
|
||||||
import fr.dcproject.voter.Vote.GRANTED
|
import fr.dcproject.security.AccessDecision.GRANTED
|
||||||
import org.amshove.kluent.`should be`
|
import org.amshove.kluent.`should be`
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
@@ -21,8 +21,8 @@ import fr.dcproject.component.workgroup.Workgroup as WorkgroupEntity
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tag("voter")
|
@Tag("security")
|
||||||
internal class WorkgroupVoterTest {
|
internal class WorkgroupAccessControlTest {
|
||||||
private val tesla = CitizenBasic(
|
private val tesla = CitizenBasic(
|
||||||
user = User(
|
user = User(
|
||||||
username = "nicolas-tesla",
|
username = "nicolas-tesla",
|
||||||
@@ -73,78 +73,78 @@ internal class WorkgroupVoterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view your workgroup`() {
|
fun `can be view your workgroup`() {
|
||||||
WorkgroupVoter()
|
WorkgroupAccessControl()
|
||||||
.canView(workgroupPublic, tesla)
|
.canView(workgroupPublic, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view your workgroup if is not public`() {
|
fun `can be view your workgroup if is not public`() {
|
||||||
WorkgroupVoter()
|
WorkgroupAccessControl()
|
||||||
.canView(workgroupAnon, tesla)
|
.canView(workgroupAnon, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view workgroup of other if is public`() {
|
fun `can be view workgroup of other if is public`() {
|
||||||
WorkgroupVoter()
|
WorkgroupAccessControl()
|
||||||
.canView(workgroupPublic, einstein)
|
.canView(workgroupPublic, einstein)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be view workgroup of other if is not public`() {
|
fun `can not be view workgroup of other if is not public`() {
|
||||||
WorkgroupVoter()
|
WorkgroupAccessControl()
|
||||||
.canView(workgroupAnon, einstein)
|
.canView(workgroupAnon, einstein)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be view your workgroup list`() {
|
fun `can be view your workgroup list`() {
|
||||||
WorkgroupVoter()
|
WorkgroupAccessControl()
|
||||||
.canView(listOf(workgroupPublic, workgroupAnon), tesla)
|
.canView(listOf(workgroupPublic, workgroupAnon), tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be create workgroup`() {
|
fun `can be create workgroup`() {
|
||||||
WorkgroupVoter()
|
WorkgroupAccessControl()
|
||||||
.canCreate(workgroupPublic, tesla)
|
.canCreate(workgroupPublic, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be create workgroup if not connected`() {
|
fun `can not be create workgroup if not connected`() {
|
||||||
WorkgroupVoter()
|
WorkgroupAccessControl()
|
||||||
.canCreate(workgroupPublic, null)
|
.canCreate(workgroupPublic, null)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be delete workgroup if owner`() {
|
fun `can be delete workgroup if owner`() {
|
||||||
WorkgroupVoter()
|
WorkgroupAccessControl()
|
||||||
.canDelete(workgroupPublic, tesla)
|
.canDelete(workgroupPublic, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be delete workgroup if not owner`() {
|
fun `can not be delete workgroup if not owner`() {
|
||||||
WorkgroupVoter()
|
WorkgroupAccessControl()
|
||||||
.canDelete(workgroupPublic, einstein)
|
.canDelete(workgroupPublic, einstein)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can be update workgroup if owner`() {
|
fun `can be update workgroup if owner`() {
|
||||||
WorkgroupVoter()
|
WorkgroupAccessControl()
|
||||||
.canUpdate(workgroupPublic, tesla)
|
.canUpdate(workgroupPublic, tesla)
|
||||||
.vote `should be` GRANTED
|
.decision `should be` GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can not be update workgroup if not owner`() {
|
fun `can not be update workgroup if not owner`() {
|
||||||
WorkgroupVoter()
|
WorkgroupAccessControl()
|
||||||
.canUpdate(workgroupPublic, einstein)
|
.canUpdate(workgroupPublic, einstein)
|
||||||
.vote `should be` DENIED
|
.decision `should be` DENIED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user