Refactors Citizen into component

Refactor CitizenVoter
Split citizens routes
This commit is contained in:
2021-01-14 15:07:59 +01:00
parent a1c1accc87
commit 6a8c5bf717
63 changed files with 404 additions and 346 deletions

View File

@@ -12,6 +12,10 @@ import fr.dcproject.component.article.route.findArticleVersions
import fr.dcproject.component.article.route.upsertArticle import fr.dcproject.component.article.route.upsertArticle
import fr.dcproject.component.article.routes.findArticles import fr.dcproject.component.article.routes.findArticles
import fr.dcproject.component.article.routes.getOneArticle import fr.dcproject.component.article.routes.getOneArticle
import fr.dcproject.component.citizen.routes.changeMyPassword
import fr.dcproject.component.citizen.routes.findCitizen
import fr.dcproject.component.citizen.routes.getCurrentCitizen
import fr.dcproject.component.citizen.routes.getOneCitizen
import fr.dcproject.elasticsearch.configElasticIndexes import fr.dcproject.elasticsearch.configElasticIndexes
import fr.dcproject.entity.User import fr.dcproject.entity.User
import fr.dcproject.event.EventNotification import fr.dcproject.event.EventNotification
@@ -71,7 +75,6 @@ fun Application.module(env: Env = PROD) {
install(AuthorizationVoter) { install(AuthorizationVoter) {
voters = listOf( voters = listOf(
ConstitutionVoter(), ConstitutionVoter(),
CitizenVoter(),
CommentVoter(), CommentVoter(),
VoteVoter(), VoteVoter(),
FollowVoter(), FollowVoter(),
@@ -151,12 +154,18 @@ fun Application.module(env: Env = PROD) {
install(Routing.Feature) { install(Routing.Feature) {
// trace { application.log.trace(it.buildText()) } // trace { application.log.trace(it.buildText()) }
authenticate(optional = true) { authenticate(optional = true) {
/* Article */
findArticles(get(), get()) findArticles(get(), get())
getOneArticle(get(), get()) getOneArticle(get(), get())
upsertArticle(get(), get(), get()) upsertArticle(get(), get(), get())
findArticleVersions(get(), get()) findArticleVersions(get(), get())
/* Citizen */
findCitizen(get(), get())
getOneCitizen(get())
getCurrentCitizen(get())
changeMyPassword(get(), get())
auth(get(), get(), get()) auth(get(), get(), get())
citizen(get(), get())
constitution(get()) constitution(get())
followArticle(get()) followArticle(get())
followConstitution(get()) followConstitution(get())

View File

@@ -1,5 +1,6 @@
package fr.dcproject package fr.dcproject
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.entity.User import fr.dcproject.entity.User
import fr.dcproject.entity.UserI import fr.dcproject.entity.UserI
import io.ktor.application.* import io.ktor.application.*
@@ -7,8 +8,7 @@ import io.ktor.auth.*
import io.ktor.util.* import io.ktor.util.*
import io.ktor.util.pipeline.* import io.ktor.util.pipeline.*
import org.koin.core.context.GlobalContext import org.koin.core.context.GlobalContext
import fr.dcproject.entity.Citizen as CitizenEntity import fr.dcproject.component.citizen.Citizen as CitizenEntity
import fr.dcproject.repository.Citizen as CitizenRepository
class ForbiddenException(message: String) : Exception(message) class ForbiddenException(message: String) : Exception(message)

View File

@@ -3,7 +3,13 @@ package fr.dcproject
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.article.ArticleRepository import fr.dcproject.component.article.ArticleRepository
import fr.dcproject.entity.* import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.CommentRef
import fr.dcproject.entity.Constitution
import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.entity.WorkgroupRef
import fr.dcproject.repository.OpinionChoice import fr.dcproject.repository.OpinionChoice
import fr.dcproject.repository.Workgroup import fr.dcproject.repository.Workgroup
import io.ktor.features.* import io.ktor.features.*
@@ -78,7 +84,7 @@ val converters: ConverterDeclaration = {
decode { values, _ -> decode { values, _ ->
val id = values.singleOrNull()?.let { UUID.fromString(it) } val id = values.singleOrNull()?.let { UUID.fromString(it) }
?: throw InternalError("Cannot convert $values to UUID") ?: throw InternalError("Cannot convert $values to UUID")
get<fr.dcproject.repository.Citizen>().findById(id) ?: throw NotFoundException("Citizen $values not found") get<fr.dcproject.component.citizen.CitizenRepository>().findById(id) ?: throw NotFoundException("Citizen $values not found")
} }
} }

View File

@@ -11,6 +11,8 @@ import com.rabbitmq.client.ConnectionFactory
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.ArticleVoter
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.component.citizen.CitizenVoter
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
@@ -27,7 +29,6 @@ import org.apache.http.HttpHost
import org.elasticsearch.client.RestClient import org.elasticsearch.client.RestClient
import org.koin.core.qualifier.named import org.koin.core.qualifier.named
import org.koin.dsl.module import org.koin.dsl.module
import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.CommentArticle as CommentArticleRepository import fr.dcproject.repository.CommentArticle as CommentArticleRepository
import fr.dcproject.repository.CommentConstitution as CommentConstitutionRepository import fr.dcproject.repository.CommentConstitution as CommentConstitutionRepository
import fr.dcproject.repository.CommentGeneric as CommentGenericRepository import fr.dcproject.repository.CommentGeneric as CommentGenericRepository
@@ -117,6 +118,7 @@ val KoinModule = module {
// Voters // Voters
single { ArticleVoter(get()) } single { ArticleVoter(get()) }
single { CitizenVoter() }
// Elasticsearch Client // Elasticsearch Client
single<RestClient> { single<RestClient> {

View File

@@ -1,5 +1,6 @@
package fr.dcproject.component.article package fr.dcproject.component.article
import fr.dcproject.component.citizen.*
import fr.dcproject.entity.* import fr.dcproject.entity.*
import fr.postgresjson.entity.* import fr.postgresjson.entity.*
import org.joda.time.DateTime import org.joda.time.DateTime

View File

@@ -1,6 +1,6 @@
package fr.dcproject.component.article package fr.dcproject.component.article
import fr.dcproject.entity.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.ViewAggregation import fr.dcproject.entity.ViewAggregation
import fr.dcproject.utils.contentToString import fr.dcproject.utils.contentToString
import fr.dcproject.utils.getJsonField import fr.dcproject.utils.getJsonField

View File

@@ -1,6 +1,6 @@
package fr.dcproject.component.article package fr.dcproject.component.article
import fr.dcproject.entity.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.voter.Voter

View File

@@ -1,6 +1,10 @@
package fr.dcproject.entity package fr.dcproject.component.citizen
import fr.dcproject.entity.CitizenI.Name import fr.dcproject.component.citizen.CitizenI.Name
import fr.dcproject.entity.User
import fr.dcproject.entity.UserI
import fr.dcproject.entity.UserRef
import fr.dcproject.entity.WorkgroupSimple
import fr.postgresjson.entity.* import fr.postgresjson.entity.*
import org.joda.time.DateTime import org.joda.time.DateTime
import java.util.* import java.util.*

View File

@@ -0,0 +1,49 @@
package fr.dcproject.component.citizen
import fr.dcproject.entity.UserI
import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester
import fr.postgresjson.repository.RepositoryI
import net.pearx.kasechange.toSnakeCase
import java.util.*
class CitizenRepository(override var requester: Requester) : RepositoryI {
fun findById(id: UUID): Citizen? = requester
.getFunction("find_citizen_by_id_with_user_and_workgroups")
.selectOne("id" to id)
fun findByUser(user: UserI): Citizen? = requester
.getFunction("find_citizen_by_user_id")
.selectOne("user_id" to user.id)
fun findByUsername(unsername: String): Citizen? = requester
.getFunction("find_citizen_by_username")
.selectOne("username" to unsername)
fun findByEmail(email: String): Citizen? = requester
.getFunction("find_citizen_by_email")
.selectOne("email" to email)
fun find(
page: Int = 1,
limit: Int = 50,
sort: String? = null,
direction: RepositoryI.Direction? = null,
search: String? = null
): Paginated<CitizenBasic> = requester
.getFunction("find_citizens")
.select(
page, limit,
"sort" to sort?.toSnakeCase(),
"direction" to direction,
"search" to search
)
fun upsert(citizen: CitizenFull): Citizen? = requester
.getFunction("upsert_citizen")
.selectOne("resource" to citizen)
fun insertWithUser(citizen: CitizenFull): Citizen? = requester
.getFunction("insert_citizen_with_user")
.selectOne("resource" to citizen)
}

View File

@@ -0,0 +1,26 @@
package fr.dcproject.component.citizen
import fr.dcproject.voter.Voter
import fr.dcproject.voter.VoterResponse
import fr.postgresjson.entity.EntityDeletedAt
class CitizenVoter : Voter() {
fun <S> canView(subjects: List<S>, connectedCitizen: CitizenI?): VoterResponse where S : CitizenI, S: EntityDeletedAt =
canAll(subjects) { canView(it, connectedCitizen) }
fun <S> canView(subject: S, connectedCitizen: CitizenI?): VoterResponse where S : CitizenI, S: EntityDeletedAt {
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")
else granted()
}
fun <S: CitizenI> canUpdate(subject: S, connectedCitizen: CitizenI?): VoterResponse {
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")
}
fun <S: CitizenI> canChangePassword(subject: S, connectedCitizen: CitizenI?): VoterResponse {
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")
}
}

View File

@@ -0,0 +1,45 @@
package fr.dcproject.component.citizen.routes
import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
import fr.dcproject.citizen
import fr.dcproject.citizenOrNull
import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenVoter
import fr.dcproject.repository.User
import fr.dcproject.voter.assert
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.http.*
import io.ktor.locations.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
@KtorExperimentalLocationsAPI
@Location("/citizens/{citizen}/password/change")
class ChangePasswordCitizenRequest(val citizen: Citizen) {
data class Input(val oldPassword: String, val newPassword: String)
}
@KtorExperimentalLocationsAPI
fun Route.changeMyPassword(voter: CitizenVoter, userRepository: User) {
put<ChangePasswordCitizenRequest> {
voter.assert { canChangePassword(it.citizen, citizenOrNull) }
try {
val content = call.receive<ChangePasswordCitizenRequest.Input>()
val currentUser = userRepository.findByCredentials(UserPasswordCredential(citizen.user.username, content.oldPassword))
val user = it.citizen.user
if (currentUser == null || currentUser.id != user.id) {
call.respond(HttpStatusCode.BadRequest, "Bad password")
} else {
user.plainPassword = content.newPassword
userRepository.changePassword(user)
call.respond(HttpStatusCode.Created)
}
} catch (e: MissingKotlinParameterException) {
call.respond(HttpStatusCode.BadRequest, "Request format is not correct")
}
}
}

View File

@@ -0,0 +1,33 @@
package fr.dcproject.component.citizen.routes
import fr.dcproject.citizenOrNull
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.component.citizen.CitizenVoter
import fr.dcproject.voter.assert
import fr.postgresjson.repository.RepositoryI
import io.ktor.application.*
import io.ktor.locations.*
import io.ktor.response.*
import io.ktor.routing.*
@KtorExperimentalLocationsAPI
@Location("/citizens")
class CitizensRequest(
page: Int = 1,
limit: Int = 50,
val sort: String? = null,
val direction: RepositoryI.Direction? = null,
val search: String? = null
) {
val page: Int = if (page < 1) 1 else page
val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit
}
@KtorExperimentalLocationsAPI
fun Route.findCitizen(voter: CitizenVoter, repo: CitizenRepository) {
get<CitizensRequest> {
val citizens = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
voter.assert { canView(citizens.result, citizenOrNull) }
call.respond(citizens)
}
}

View File

@@ -0,0 +1,28 @@
package fr.dcproject.component.citizen.routes
import fr.dcproject.citizen
import fr.dcproject.citizenOrNull
import fr.dcproject.component.citizen.CitizenVoter
import fr.dcproject.voter.assert
import io.ktor.application.*
import io.ktor.http.*
import io.ktor.locations.*
import io.ktor.response.*
import io.ktor.routing.*
@KtorExperimentalLocationsAPI
@Location("/citizens/current")
class CurrentCitizenRequest
@KtorExperimentalLocationsAPI
fun Route.getCurrentCitizen(voter: CitizenVoter) {
get<CurrentCitizenRequest> {
val currentUser = citizenOrNull
if (currentUser === null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
voter.assert { canView(currentUser, citizenOrNull) }
call.respond(citizen)
}
}
}

View File

@@ -0,0 +1,23 @@
package fr.dcproject.component.citizen.routes
import fr.dcproject.citizenOrNull
import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenVoter
import fr.dcproject.voter.assert
import io.ktor.application.*
import io.ktor.locations.*
import io.ktor.response.*
import io.ktor.routing.*
@KtorExperimentalLocationsAPI
@Location("/citizens/{citizen}")
class CitizenRequest(val citizen: Citizen)
@KtorExperimentalLocationsAPI
fun Route.getOneCitizen(voter: CitizenVoter) {
get<CitizenRequest> {
voter.assert { canView(it.citizen, citizenOrNull) }
call.respond(it.citizen)
}
}

View File

@@ -1,5 +1,6 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.dcproject.component.citizen.CitizenRef
import fr.postgresjson.entity.* import fr.postgresjson.entity.*
import org.joda.time.DateTime import org.joda.time.DateTime
import java.util.* import java.util.*

View File

@@ -2,6 +2,8 @@ package fr.dcproject.entity
import fr.dcproject.component.article.ArticleI import fr.dcproject.component.article.ArticleI
import fr.dcproject.component.article.ArticleSimple import fr.dcproject.component.article.ArticleSimple
import fr.dcproject.component.citizen.CitizenSimple
import fr.dcproject.component.citizen.CitizenWithUserI
import fr.postgresjson.entity.* import fr.postgresjson.entity.*
import java.util.* import java.util.*

View File

@@ -1,5 +1,6 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.dcproject.component.citizen.CitizenI
import fr.postgresjson.entity.EntityCreatedBy import fr.postgresjson.entity.EntityCreatedBy
import fr.postgresjson.entity.EntityI import fr.postgresjson.entity.EntityI

View File

@@ -1,6 +1,7 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.CitizenI
import fr.postgresjson.entity.EntityCreatedAt import fr.postgresjson.entity.EntityCreatedAt
import fr.postgresjson.entity.EntityCreatedBy import fr.postgresjson.entity.EntityCreatedBy
import fr.postgresjson.entity.UuidEntity import fr.postgresjson.entity.UuidEntity

View File

@@ -1,5 +1,8 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenBasicI
import fr.dcproject.component.citizen.CitizenI
import fr.postgresjson.entity.* import fr.postgresjson.entity.*
import java.util.* import java.util.*

View File

@@ -1,6 +1,10 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenBasicI
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef
import fr.postgresjson.entity.* import fr.postgresjson.entity.*
import java.util.* import java.util.*

View File

@@ -1,5 +1,8 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenBasicI
import fr.dcproject.component.citizen.CitizenI
import fr.postgresjson.entity.* import fr.postgresjson.entity.*
import java.util.* import java.util.*
@Deprecated("") @Deprecated("")

View File

@@ -1,5 +1,8 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.dcproject.component.citizen.CitizenBasicI
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenWithUserI
import fr.dcproject.entity.WorkgroupWithMembersI.Member import fr.dcproject.entity.WorkgroupWithMembersI.Member
import fr.dcproject.entity.WorkgroupWithMembersI.Member.Role import fr.dcproject.entity.WorkgroupWithMembersI.Member.Role
import fr.postgresjson.entity.* import fr.postgresjson.entity.*

View File

@@ -4,7 +4,7 @@ import com.rabbitmq.client.*
import com.rabbitmq.client.BuiltinExchangeType.DIRECT import com.rabbitmq.client.BuiltinExchangeType.DIRECT
import fr.dcproject.Config import fr.dcproject.Config
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.entity.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.FollowSimple import fr.dcproject.entity.FollowSimple
import fr.dcproject.entity.TargetRef import fr.dcproject.entity.TargetRef
import fr.dcproject.event.publisher.Publisher import fr.dcproject.event.publisher.Publisher

View File

@@ -5,13 +5,13 @@ import com.sendgrid.helpers.mail.objects.Content
import com.sendgrid.helpers.mail.objects.Email import com.sendgrid.helpers.mail.objects.Email
import fr.dcproject.component.article.ArticleRepository import fr.dcproject.component.article.ArticleRepository
import fr.dcproject.component.article.ArticleWithTitleI import fr.dcproject.component.article.ArticleWithTitleI
import fr.dcproject.entity.CitizenBasicI import fr.dcproject.component.citizen.CitizenBasicI
import fr.dcproject.entity.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.entity.FollowSimple import fr.dcproject.entity.FollowSimple
import fr.dcproject.entity.TargetRef import fr.dcproject.entity.TargetRef
import fr.postgresjson.entity.UuidEntityI import fr.postgresjson.entity.UuidEntityI
import java.util.* import java.util.*
import fr.dcproject.repository.Citizen as CitizenRepository
class NotificationEmailSender( class NotificationEmailSender(
private val mailer: Mailer, private val mailer: Mailer,

View File

@@ -4,9 +4,9 @@ import com.sendgrid.helpers.mail.Mail
import com.sendgrid.helpers.mail.objects.Content import com.sendgrid.helpers.mail.objects.Content
import com.sendgrid.helpers.mail.objects.Email import com.sendgrid.helpers.mail.objects.Email
import fr.dcproject.JwtConfig import fr.dcproject.JwtConfig
import fr.dcproject.entity.CitizenBasicI import fr.dcproject.component.citizen.CitizenBasicI
import io.ktor.http.URLBuilder import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.repository.Citizen as CitizenRepository import io.ktor.http.*
class SsoManager( class SsoManager(
private val mailer: Mailer, private val mailer: Mailer,

View File

@@ -1,53 +0,0 @@
package fr.dcproject.repository
import fr.dcproject.entity.CitizenBasic
import fr.dcproject.entity.CitizenFull
import fr.dcproject.entity.UserI
import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester
import fr.postgresjson.repository.RepositoryI
import fr.postgresjson.repository.RepositoryI.Direction
import net.pearx.kasechange.toSnakeCase
import java.util.*
import fr.dcproject.entity.Citizen as CitizenEntity
class Citizen(override var requester: Requester) : RepositoryI {
fun findById(id: UUID): CitizenEntity? = requester
.getFunction("find_citizen_by_id_with_user_and_workgroups")
.selectOne("id" to id)
fun findByUser(user: UserI): CitizenEntity? = requester
.getFunction("find_citizen_by_user_id")
.selectOne("user_id" to user.id)
fun findByUsername(unsername: String): CitizenEntity? = requester
.getFunction("find_citizen_by_username")
.selectOne("username" to unsername)
fun findByEmail(email: String): CitizenEntity? = requester
.getFunction("find_citizen_by_email")
.selectOne("email" to email)
fun find(
page: Int = 1,
limit: Int = 50,
sort: String? = null,
direction: Direction? = null,
search: String? = null
): Paginated<CitizenBasic> = requester
.getFunction("find_citizens")
.select(
page, limit,
"sort" to sort?.toSnakeCase(),
"direction" to direction,
"search" to search
)
fun upsert(citizen: CitizenFull): CitizenEntity? = requester
.getFunction("upsert_citizen")
.selectOne("resource" to citizen)
fun insertWithUser(citizen: CitizenFull): CitizenEntity? = requester
.getFunction("insert_citizen_with_user")
.selectOne("resource" to citizen)
}

View File

@@ -2,6 +2,8 @@ package fr.dcproject.repository
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.* import fr.dcproject.entity.*
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester

View File

@@ -1,7 +1,7 @@
package fr.dcproject.repository package fr.dcproject.repository
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.entity.CitizenWithUserI import fr.dcproject.component.citizen.CitizenWithUserI
import fr.dcproject.entity.ConstitutionSimple import fr.dcproject.entity.ConstitutionSimple
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester

View File

@@ -2,7 +2,12 @@ package fr.dcproject.repository
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.entity.* import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.entity.FollowForUpdate
import fr.dcproject.entity.FollowSimple
import fr.dcproject.entity.TargetRef
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.UuidEntity import fr.postgresjson.entity.UuidEntity

View File

@@ -2,7 +2,7 @@ package fr.dcproject.repository
import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.core.type.TypeReference
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.entity.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.OpinionChoiceRef import fr.dcproject.entity.OpinionChoiceRef
import fr.dcproject.entity.OpinionForUpdate import fr.dcproject.entity.OpinionForUpdate
import fr.dcproject.entity.TargetRef import fr.dcproject.entity.TargetRef
@@ -11,7 +11,7 @@ import fr.postgresjson.connexion.Requester
import fr.postgresjson.repository.RepositoryI import fr.postgresjson.repository.RepositoryI
import net.pearx.kasechange.toSnakeCase import net.pearx.kasechange.toSnakeCase
import java.util.* import java.util.*
import fr.dcproject.entity.Citizen as CitizenEntity import fr.dcproject.component.citizen.Citizen as CitizenEntity
import fr.dcproject.entity.Opinion as OpinionEntity import fr.dcproject.entity.Opinion as OpinionEntity
import fr.dcproject.entity.OpinionArticle as OpinionArticleEntity import fr.dcproject.entity.OpinionArticle as OpinionArticleEntity
import fr.dcproject.entity.OpinionChoice as OpinionChoiceEntity import fr.dcproject.entity.OpinionChoice as OpinionChoiceEntity

View File

@@ -2,13 +2,14 @@ package fr.dcproject.repository
import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.core.type.TypeReference
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.* import fr.dcproject.entity.*
import fr.dcproject.entity.Constitution import fr.dcproject.entity.Constitution
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.repository.RepositoryI import fr.postgresjson.repository.RepositoryI
import java.util.* import java.util.*
import fr.dcproject.entity.Citizen as CitizenEntity import fr.dcproject.component.citizen.Citizen as CitizenEntity
import fr.dcproject.entity.Vote as VoteEntity import fr.dcproject.entity.Vote as VoteEntity
open class Vote<T : TargetI>(override var requester: Requester) : RepositoryI { open class Vote<T : TargetI>(override var requester: Requester) : RepositoryI {

View File

@@ -1,6 +1,11 @@
package fr.dcproject.repository package fr.dcproject.repository
import fr.dcproject.entity.* import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.WorkgroupI
import fr.dcproject.entity.WorkgroupRef
import fr.dcproject.entity.WorkgroupSimple
import fr.dcproject.entity.WorkgroupWithMembersI
import fr.dcproject.entity.WorkgroupWithMembersI.Member import fr.dcproject.entity.WorkgroupWithMembersI.Member
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester

View File

@@ -3,25 +3,22 @@ package fr.dcproject.routes
import com.fasterxml.jackson.databind.exc.MismatchedInputException import com.fasterxml.jackson.databind.exc.MismatchedInputException
import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
import fr.dcproject.JwtConfig import fr.dcproject.JwtConfig
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.entity.UserI.Roles.ROLE_USER import fr.dcproject.entity.UserI.Roles.ROLE_USER
import fr.dcproject.messages.SsoManager import fr.dcproject.messages.SsoManager
import fr.dcproject.routes.AuthPaths.LoginRequest import fr.dcproject.routes.AuthPaths.LoginRequest
import fr.dcproject.routes.AuthPaths.RegisterRequest import fr.dcproject.routes.AuthPaths.RegisterRequest
import fr.dcproject.routes.AuthPaths.SsoRequest import fr.dcproject.routes.AuthPaths.SsoRequest
import io.ktor.application.call import io.ktor.application.*
import io.ktor.auth.UserPasswordCredential import io.ktor.auth.*
import io.ktor.features.BadRequestException import io.ktor.features.*
import io.ktor.http.HttpStatusCode import io.ktor.http.*
import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.locations.*
import io.ktor.locations.Location import io.ktor.request.*
import io.ktor.locations.post import io.ktor.response.*
import io.ktor.request.receive import io.ktor.routing.*
import io.ktor.response.respond import io.ktor.util.*
import io.ktor.response.respondText import fr.dcproject.component.citizen.Citizen as CitizenEntity
import io.ktor.routing.Route
import io.ktor.util.KtorExperimentalAPI
import fr.dcproject.entity.Citizen as CitizenEntity
import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.User as UserRepository import fr.dcproject.repository.User as UserRepository
@KtorExperimentalLocationsAPI @KtorExperimentalLocationsAPI

View File

@@ -1,96 +0,0 @@
package fr.dcproject.routes
import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
import fr.dcproject.citizen
import fr.dcproject.citizenOrNull
import fr.dcproject.entity.Citizen
import fr.dcproject.routes.CitizenPaths.ChangePasswordCitizenRequest
import fr.dcproject.routes.CitizenPaths.CitizenRequest
import fr.dcproject.routes.CitizenPaths.CitizensRequest
import fr.dcproject.routes.CitizenPaths.CurrentCitizenRequest
import fr.dcproject.security.voter.CitizenVoter.Action.CHANGE_PASSWORD
import fr.dcproject.security.voter.CitizenVoter.Action.VIEW
import fr.ktorVoter.assertCan
import fr.ktorVoter.assertCanAll
import fr.postgresjson.repository.RepositoryI.Direction
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.http.*
import io.ktor.locations.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.User as UserRepository
@KtorExperimentalLocationsAPI
object CitizenPaths {
@Location("/citizens")
class CitizensRequest(
page: Int = 1,
limit: Int = 50,
val sort: String? = null,
val direction: Direction? = null,
val search: String? = null
) {
val page: Int = if (page < 1) 1 else page
val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit
}
@Location("/citizens/{citizen}")
class CitizenRequest(val citizen: Citizen)
@Location("/citizens/current")
class CurrentCitizenRequest
@Location("/citizens/{citizen}/password/change")
class ChangePasswordCitizenRequest(val citizen: Citizen) {
data class Content(val oldPassword: String, val newPassword: String)
}
}
@KtorExperimentalLocationsAPI
fun Route.citizen(
repo: CitizenRepository,
userRepository: UserRepository
) {
get<CitizensRequest> {
val citizens = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
assertCanAll(VIEW, citizens.result)
call.respond(citizens)
}
get<CitizenRequest> {
assertCan(VIEW, it.citizen)
call.respond(it.citizen)
}
get<CurrentCitizenRequest> {
if (citizenOrNull === null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
assertCan(VIEW, citizen)
call.respond(citizen)
}
}
put<ChangePasswordCitizenRequest> {
assertCan(CHANGE_PASSWORD, it.citizen)
try {
val content = call.receive<ChangePasswordCitizenRequest.Content>()
val currentUser = userRepository.findByCredentials(UserPasswordCredential(citizen.user.username, content.oldPassword))
val user = it.citizen.user
if (currentUser == null || currentUser.id != user.id) {
call.respond(HttpStatusCode.BadRequest, "Bad password")
} else {
user.plainPassword = content.newPassword
userRepository.changePassword(user)
call.respond(HttpStatusCode.Created)
}
} catch (e: MissingKotlinParameterException) {
call.respond(HttpStatusCode.BadRequest, "Request format is not correct")
}
}
}

View File

@@ -3,7 +3,7 @@ package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.entity.Citizen import fr.dcproject.component.citizen.Citizen
import fr.dcproject.entity.CommentForUpdate import fr.dcproject.entity.CommentForUpdate
import fr.dcproject.repository.CommentArticle.Sort import fr.dcproject.repository.CommentArticle.Sort
import fr.dcproject.security.voter.CommentVoter.Action.CREATE import fr.dcproject.security.voter.CommentVoter.Action.CREATE

View File

@@ -1,7 +1,7 @@
package fr.dcproject.routes package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.entity.Citizen import fr.dcproject.component.citizen.Citizen
import fr.dcproject.entity.CommentForUpdate import fr.dcproject.entity.CommentForUpdate
import fr.dcproject.entity.ConstitutionRef import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.security.voter.CommentVoter.Action.CREATE import fr.dcproject.security.voter.CommentVoter.Action.CREATE

View File

@@ -2,7 +2,7 @@ package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.entity.CitizenWithUserI import fr.dcproject.component.citizen.CitizenWithUserI
import fr.dcproject.entity.ConstitutionSimple import fr.dcproject.entity.ConstitutionSimple
import fr.dcproject.entity.ConstitutionSimple.TitleSimple import fr.dcproject.entity.ConstitutionSimple.TitleSimple
import fr.dcproject.security.voter.ConstitutionVoter.Action.CREATE import fr.dcproject.security.voter.ConstitutionVoter.Action.CREATE

View File

@@ -2,7 +2,7 @@ package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.entity.Citizen import fr.dcproject.component.citizen.Citizen
import fr.dcproject.entity.FollowForUpdate import fr.dcproject.entity.FollowForUpdate
import fr.dcproject.security.voter.FollowVoter.Action.* import fr.dcproject.security.voter.FollowVoter.Action.*
import fr.ktorVoter.assertCan import fr.ktorVoter.assertCan

View File

@@ -1,7 +1,7 @@
package fr.dcproject.routes package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.entity.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.ConstitutionRef import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.entity.FollowForUpdate import fr.dcproject.entity.FollowForUpdate
import fr.dcproject.security.voter.FollowVoter.Action.* import fr.dcproject.security.voter.FollowVoter.Action.*

View File

@@ -2,7 +2,7 @@ package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.entity.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.OpinionChoiceRef import fr.dcproject.entity.OpinionChoiceRef
import fr.dcproject.security.voter.OpinionVoter.Action.CREATE import fr.dcproject.security.voter.OpinionVoter.Action.CREATE
import fr.dcproject.security.voter.OpinionVoter.Action.VIEW import fr.dcproject.security.voter.OpinionVoter.Action.VIEW
@@ -19,7 +19,7 @@ import io.ktor.util.*
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
import org.koin.core.get import org.koin.core.get
import java.util.* import java.util.*
import fr.dcproject.entity.Citizen as CitizenEntity import fr.dcproject.component.citizen.Citizen as CitizenEntity
import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository
@KtorExperimentalLocationsAPI @KtorExperimentalLocationsAPI

View File

@@ -2,7 +2,7 @@ package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.entity.Citizen import fr.dcproject.component.citizen.Citizen
import fr.dcproject.entity.VoteForUpdate import fr.dcproject.entity.VoteForUpdate
import fr.dcproject.repository.CommentGeneric import fr.dcproject.repository.CommentGeneric
import fr.dcproject.repository.VoteComment import fr.dcproject.repository.VoteComment

View File

@@ -1,7 +1,7 @@
package fr.dcproject.routes package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.entity.Citizen import fr.dcproject.component.citizen.Citizen
import fr.dcproject.entity.VoteForUpdate import fr.dcproject.entity.VoteForUpdate
import fr.dcproject.routes.VoteConstitutionPaths.ConstitutionVoteRequest.Content import fr.dcproject.routes.VoteConstitutionPaths.ConstitutionVoteRequest.Content
import fr.dcproject.security.voter.VoteVoter.Action.CREATE import fr.dcproject.security.voter.VoteVoter.Action.CREATE

View File

@@ -1,7 +1,7 @@
package fr.dcproject.routes package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.entity.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.WorkgroupSimple import fr.dcproject.entity.WorkgroupSimple
import fr.dcproject.entity.WorkgroupWithMembersI.Member import fr.dcproject.entity.WorkgroupWithMembersI.Member
import fr.dcproject.entity.WorkgroupWithMembersI.Member.Role import fr.dcproject.entity.WorkgroupWithMembersI.Member.Role

View File

@@ -1,6 +1,6 @@
package fr.dcproject.views package fr.dcproject.views
import fr.dcproject.entity.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.ViewAggregation import fr.dcproject.entity.ViewAggregation
import org.elasticsearch.client.Response import org.elasticsearch.client.Response
import org.joda.time.DateTime import org.joda.time.DateTime

View File

@@ -1,61 +0,0 @@
package fr.dcproject.security.voter
import fr.dcproject.entity.CitizenBasicI
import fr.dcproject.entity.CitizenWithUserI
import fr.dcproject.user
import fr.dcproject.voter.NoRuleDefinedException
import fr.dcproject.voter.NoSubjectDefinedException
import fr.ktorVoter.*
import io.ktor.application.*
import io.ktor.locations.*
@KtorExperimentalLocationsAPI
class CitizenVoter : Voter<ApplicationCall> {
enum class Action : ActionI {
CREATE,
UPDATE,
VIEW,
DELETE,
CHANGE_PASSWORD
}
override fun invoke(action: Any, context: ApplicationCall, subject: Any?): VoterResponseI {
if (!((action is Action)
&& (subject is CitizenBasicI?))) return abstain()
val user = context.user
if (action == Action.CREATE && user != null) {
return granted()
}
if (action == Action.VIEW) {
if (user == null) return denied("You must be connected to view citizen", "citizen.view.connected")
if (subject is CitizenBasicI) {
return if (subject.isDeleted()) denied("You cannot view a deleted citizen", "citizen.view.deleted")
else granted()
}
throw NoRuleDefinedException(action)
}
if (action == Action.DELETE) {
return denied("You can never deleted a citizen", "citizen.delete.never")
}
if (action == Action.UPDATE) {
if (user == null) return denied("You must be connected to update Citizen", "citizen.update.notConnected")
if (subject !is CitizenWithUserI) throw NoSubjectDefinedException(action)
return if (subject.user.id == user.id) granted() else denied("You can only update your citizen", "citizen.update.notYours")
}
if (action == Action.CHANGE_PASSWORD && user != null && subject is CitizenBasicI) {
val userToChange = subject.user
return if (user.id == userToChange.id) {
granted()
} else {
denied("You can only change your password", "citizen.password.notYours")
}
}
throw NoRuleDefinedException(action)
}
}

View File

@@ -1,7 +1,7 @@
package fr.dcproject.security.voter package fr.dcproject.security.voter
import fr.dcproject.citizenOrNull import fr.dcproject.citizenOrNull
import fr.dcproject.entity.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.FollowI import fr.dcproject.entity.FollowI
import fr.dcproject.voter.NoSubjectDefinedException import fr.dcproject.voter.NoSubjectDefinedException
import fr.ktorVoter.* import fr.ktorVoter.*

View File

@@ -1,7 +1,7 @@
import fr.dcproject.Env import fr.dcproject.Env
import fr.dcproject.component.article.ArticleRefVersioning import fr.dcproject.component.article.ArticleRefVersioning
import fr.dcproject.component.article.ArticleViewManager import fr.dcproject.component.article.ArticleViewManager
import fr.dcproject.entity.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.module import fr.dcproject.module
import io.ktor.locations.* import io.ktor.locations.*
import io.ktor.server.testing.* import io.ktor.server.testing.*

View File

@@ -3,7 +3,11 @@ package feature
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.entity.* import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.entity.CommentForUpdate
import fr.dcproject.entity.WorkgroupRef
import fr.dcproject.repository.CommentArticle import fr.dcproject.repository.CommentArticle
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
import io.cucumber.datatable.DataTable import io.cucumber.datatable.DataTable
@@ -13,7 +17,6 @@ import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import java.util.* import java.util.*
import fr.dcproject.entity.User as UserEntity import fr.dcproject.entity.User as UserEntity
import fr.dcproject.repository.Citizen as CitizenRepository
class ArticleSteps : En, KoinTest { class ArticleSteps : En, KoinTest {
init { init {

View File

@@ -1,7 +1,8 @@
package feature package feature
import fr.dcproject.entity.Citizen import fr.dcproject.component.citizen.Citizen
import fr.dcproject.entity.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.entity.User import fr.dcproject.entity.User
import io.cucumber.datatable.DataTable import io.cucumber.datatable.DataTable
import io.cucumber.java8.En import io.cucumber.java8.En
@@ -9,7 +10,6 @@ import org.joda.time.DateTime
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import java.util.* import java.util.*
import fr.dcproject.repository.Citizen as CitizenRepository
class CitizenSteps : En, KoinTest { class CitizenSteps : En, KoinTest {
init { init {

View File

@@ -1,7 +1,13 @@
package feature package feature
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.entity.* import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.component.citizen.CitizenWithUserI
import fr.dcproject.entity.CommentForUpdate
import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.entity.ConstitutionSimple
import fr.dcproject.repository.CommentConstitution import fr.dcproject.repository.CommentConstitution
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
import io.cucumber.datatable.DataTable import io.cucumber.datatable.DataTable
@@ -11,7 +17,6 @@ import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import java.util.* import java.util.*
import fr.dcproject.entity.User as UserEntity import fr.dcproject.entity.User as UserEntity
import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.Constitution as ConstitutionRepository import fr.dcproject.repository.Constitution as ConstitutionRepository
class ConstitutionSteps : En, KoinTest { class ConstitutionSteps : En, KoinTest {

View File

@@ -1,13 +1,13 @@
package feature package feature
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.entity.ConstitutionRef import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.entity.FollowForUpdate import fr.dcproject.entity.FollowForUpdate
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
import io.cucumber.java8.En import io.cucumber.java8.En
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.FollowArticle as FollowArticleRepository import fr.dcproject.repository.FollowArticle as FollowArticleRepository
import fr.dcproject.repository.FollowConstitution as FollowConstitutionRepository import fr.dcproject.repository.FollowConstitution as FollowConstitutionRepository

View File

@@ -2,11 +2,11 @@ package feature
import com.auth0.jwt.JWT import com.auth0.jwt.JWT
import fr.dcproject.JwtConfig import fr.dcproject.JwtConfig
import fr.dcproject.component.citizen.CitizenRepository
import io.cucumber.java8.En import io.cucumber.java8.En
import io.ktor.http.* import io.ktor.http.*
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import fr.dcproject.repository.Citizen as CitizenRepository
class KtorServerAuthSteps : En, KoinTest { class KtorServerAuthSteps : En, KoinTest {
init { init {

View File

@@ -2,6 +2,7 @@ package feature
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.article.ArticleRepository import fr.dcproject.component.article.ArticleRepository
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.entity.OpinionChoice import fr.dcproject.entity.OpinionChoice
import fr.dcproject.entity.OpinionForUpdate import fr.dcproject.entity.OpinionForUpdate
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
@@ -10,7 +11,6 @@ import io.cucumber.java8.En
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import java.util.* import java.util.*
import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.OpinionArticle as OpinionRepository import fr.dcproject.repository.OpinionArticle as OpinionRepository
import fr.dcproject.repository.OpinionChoice as OpinionChoiceRepository import fr.dcproject.repository.OpinionChoice as OpinionChoiceRepository

View File

@@ -1,13 +1,13 @@
package feature package feature
import fr.dcproject.component.article.ArticleRepository import fr.dcproject.component.article.ArticleRepository
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.entity.VoteForUpdate import fr.dcproject.entity.VoteForUpdate
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
import io.cucumber.java8.En import io.cucumber.java8.En
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import java.util.* import java.util.*
import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.VoteArticle as VoteRepository import fr.dcproject.repository.VoteArticle as VoteRepository
class VoteSteps : En, KoinTest { class VoteSteps : En, KoinTest {

View File

@@ -1,5 +1,9 @@
package feature package feature
import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.entity.* import fr.dcproject.entity.*
import fr.dcproject.entity.WorkgroupWithMembersI.Member import fr.dcproject.entity.WorkgroupWithMembersI.Member
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
@@ -10,7 +14,6 @@ import org.junit.Assert
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import java.util.* import java.util.*
import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.Workgroup as WorkgroupRepository import fr.dcproject.repository.Workgroup as WorkgroupRepository
class WorkgroupSteps : En, KoinTest { class WorkgroupSteps : En, KoinTest {

View File

@@ -2,8 +2,8 @@ package fr.dcproject.security.voter
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleVoter import fr.dcproject.component.article.ArticleVoter
import fr.dcproject.entity.CitizenCart import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.entity.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.User import fr.dcproject.entity.User
import fr.dcproject.entity.UserI import fr.dcproject.entity.UserI
import fr.dcproject.voter.Vote.DENIED import fr.dcproject.voter.Vote.DENIED

View File

@@ -1,22 +1,19 @@
package fr.dcproject.security.voter package fr.dcproject.security.voter
import fr.dcproject.entity.CitizenBasic import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.entity.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenVoter
import fr.dcproject.entity.User import fr.dcproject.entity.User
import fr.dcproject.entity.UserI import fr.dcproject.entity.UserI
import fr.dcproject.user import fr.dcproject.voter.Vote.DENIED
import fr.ktorVoter.* import fr.dcproject.voter.Vote.GRANTED
import io.ktor.application.*
import io.ktor.locations.* import io.ktor.locations.*
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic import io.mockk.mockkStatic
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
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.assertThrows
@KtorExperimentalLocationsAPI @KtorExperimentalLocationsAPI
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
@@ -57,78 +54,51 @@ class CitizenVoterTest {
} }
@Test @Test
fun `support citizen`(): Unit = CitizenVoter().run { fun `can be view the citizen`() {
val p = object : ActionI {} CitizenVoter()
mockk<ApplicationCall> { .canView(subject = einstein, connectedCitizen = tesla)
every { user } returns tesla.user .vote `should be` GRANTED
}.let {
this(CitizenVoter.Action.VIEW, it, einstein).vote `should be` Vote.GRANTED
this(p, it, einstein).vote `should be` Vote.ABSTAIN
}
} }
@Test @Test
fun `can be view the citizen`(): Unit = listOf(CitizenVoter()).run { fun `can be view the citizen list`() {
mockk<ApplicationCall> { CitizenVoter()
every { user } returns tesla.user .canView(subjects = listOf(tesla, einstein), connectedCitizen = einstein)
}.let { .vote `should be` GRANTED
can(CitizenVoter.Action.VIEW, it, einstein) `should be` true
}
} }
@Test @Test
fun `can be view the citizen list`(): Unit = listOf(CitizenVoter()).run { fun `can not view deleted citizen`() {
mockk<ApplicationCall> { CitizenVoter()
every { user } returns einstein.user .canView(subject = curie, connectedCitizen = tesla)
}.let { .vote `should be` DENIED
canAll(CitizenVoter.Action.VIEW, it, listOf(einstein, tesla)) `should be` true
}
} }
@Test @Test
fun `can not view deleted citizen`(): Unit = listOf(CitizenVoter()).run { fun `can be update itself`() {
mockk<ApplicationCall> { CitizenVoter()
every { user } returns tesla.user .canUpdate(subject = einstein, connectedCitizen = einstein)
}.let { .vote `should be` GRANTED
can(CitizenVoter.Action.VIEW, it, curie) `should be` false
}
} }
@Test @Test
fun `can be update itself`(): Unit = listOf(CitizenVoter()).run { fun `can not be update other citizen`() {
mockk<ApplicationCall> { CitizenVoter()
every { user } returns einstein.user .canUpdate(subject = tesla, connectedCitizen = einstein)
}.let { .vote `should be` DENIED
can(CitizenVoter.Action.UPDATE, it, einstein) `should be` true
}
} }
@Test @Test
fun `can not be update other citizen`(): Unit = listOf(CitizenVoter()).run { fun `can be change password of itself`() {
mockk<ApplicationCall> { CitizenVoter()
every { user } returns einstein.user .canChangePassword(subject = einstein, connectedCitizen = einstein)
}.let { .vote `should be` GRANTED
assertThrows<OneOrMoreVoterDeniedException> {
assertCan(CitizenVoter.Action.UPDATE, it, tesla)
}
}
} }
@Test @Test
fun `can be change password of itself`(): Unit = listOf(CitizenVoter()).run { fun `can not be change password of other citizen`() {
mockk<ApplicationCall> { CitizenVoter()
every { user } returns einstein.user .canChangePassword(subject = tesla, connectedCitizen = einstein)
}.let { .vote `should be` DENIED
can(CitizenVoter.Action.CHANGE_PASSWORD, it, einstein) `should be` true
}
}
@Test
fun `can not be change password of other citizen`(): Unit = listOf(CitizenVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(CitizenVoter.Action.CHANGE_PASSWORD, it, tesla) `should be` false
}
} }
} }

View File

@@ -3,10 +3,15 @@ package fr.dcproject.security.voter
import fr.dcproject.citizenOrNull import fr.dcproject.citizenOrNull
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.entity.* import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.CommentForUpdate
import fr.dcproject.entity.CommentForView
import fr.dcproject.entity.User
import fr.dcproject.entity.UserI
import fr.dcproject.voter.NoSubjectDefinedException import fr.dcproject.voter.NoSubjectDefinedException
import fr.ktorVoter.* import fr.ktorVoter.*
import fr.ktorVoter.Vote
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import io.ktor.application.* import io.ktor.application.*
import io.ktor.locations.* import io.ktor.locations.*

View File

@@ -2,7 +2,13 @@ package fr.dcproject.security.voter
import fr.dcproject.citizenOrNull import fr.dcproject.citizenOrNull
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.entity.* import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.Follow
import fr.dcproject.entity.User
import fr.dcproject.entity.UserI
import fr.dcproject.voter.NoSubjectDefinedException import fr.dcproject.voter.NoSubjectDefinedException
import fr.ktorVoter.ActionI import fr.ktorVoter.ActionI
import fr.ktorVoter.Vote import fr.ktorVoter.Vote

View File

@@ -1,7 +1,12 @@
package fr.dcproject.security.voter package fr.dcproject.security.voter
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.entity.* import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.OpinionChoice
import fr.dcproject.entity.User
import fr.dcproject.entity.UserI
import fr.dcproject.user import fr.dcproject.user
import fr.ktorVoter.ActionI import fr.ktorVoter.ActionI
import fr.ktorVoter.Vote import fr.ktorVoter.Vote

View File

@@ -1,11 +1,16 @@
package fr.dcproject.security.voter package fr.dcproject.security.voter
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.entity.* import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.Opinion
import fr.dcproject.entity.OpinionChoice
import fr.dcproject.entity.User
import fr.dcproject.entity.UserI
import fr.dcproject.user import fr.dcproject.user
import fr.dcproject.voter.NoSubjectDefinedException import fr.dcproject.voter.NoSubjectDefinedException
import fr.ktorVoter.* import fr.ktorVoter.*
import fr.ktorVoter.Vote
import io.ktor.application.* import io.ktor.application.*
import io.ktor.locations.* import io.ktor.locations.*
import io.mockk.every import io.mockk.every

View File

@@ -3,7 +3,13 @@ package fr.dcproject.security.voter
import fr.dcproject.citizenOrNull import fr.dcproject.citizenOrNull
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.entity.* import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.User
import fr.dcproject.entity.UserI
import fr.dcproject.entity.VoteForUpdate
import fr.dcproject.voter.NoSubjectDefinedException import fr.dcproject.voter.NoSubjectDefinedException
import fr.ktorVoter.ActionI import fr.ktorVoter.ActionI
import fr.ktorVoter.Vote import fr.ktorVoter.Vote

View File

@@ -1,7 +1,13 @@
package fr.dcproject.security.voter package fr.dcproject.security.voter
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.entity.* import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.User
import fr.dcproject.entity.UserI
import fr.dcproject.entity.WorkgroupRef
import fr.dcproject.entity.WorkgroupWithMembersI
import fr.dcproject.user import fr.dcproject.user
import fr.dcproject.voter.NoSubjectDefinedException import fr.dcproject.voter.NoSubjectDefinedException
import fr.ktorVoter.ActionI import fr.ktorVoter.ActionI