Refactors Citizen into component
Refactor CitizenVoter Split citizens routes
This commit is contained in:
@@ -12,6 +12,10 @@ import fr.dcproject.component.article.route.findArticleVersions
|
||||
import fr.dcproject.component.article.route.upsertArticle
|
||||
import fr.dcproject.component.article.routes.findArticles
|
||||
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.entity.User
|
||||
import fr.dcproject.event.EventNotification
|
||||
@@ -71,7 +75,6 @@ fun Application.module(env: Env = PROD) {
|
||||
install(AuthorizationVoter) {
|
||||
voters = listOf(
|
||||
ConstitutionVoter(),
|
||||
CitizenVoter(),
|
||||
CommentVoter(),
|
||||
VoteVoter(),
|
||||
FollowVoter(),
|
||||
@@ -151,12 +154,18 @@ fun Application.module(env: Env = PROD) {
|
||||
install(Routing.Feature) {
|
||||
// trace { application.log.trace(it.buildText()) }
|
||||
authenticate(optional = true) {
|
||||
/* Article */
|
||||
findArticles(get(), get())
|
||||
getOneArticle(get(), get())
|
||||
upsertArticle(get(), get(), get())
|
||||
findArticleVersions(get(), get())
|
||||
/* Citizen */
|
||||
findCitizen(get(), get())
|
||||
getOneCitizen(get())
|
||||
getCurrentCitizen(get())
|
||||
changeMyPassword(get(), get())
|
||||
|
||||
auth(get(), get(), get())
|
||||
citizen(get(), get())
|
||||
constitution(get())
|
||||
followArticle(get())
|
||||
followConstitution(get())
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package fr.dcproject
|
||||
|
||||
import fr.dcproject.component.citizen.CitizenRepository
|
||||
import fr.dcproject.entity.User
|
||||
import fr.dcproject.entity.UserI
|
||||
import io.ktor.application.*
|
||||
@@ -7,8 +8,7 @@ import io.ktor.auth.*
|
||||
import io.ktor.util.*
|
||||
import io.ktor.util.pipeline.*
|
||||
import org.koin.core.context.GlobalContext
|
||||
import fr.dcproject.entity.Citizen as CitizenEntity
|
||||
import fr.dcproject.repository.Citizen as CitizenRepository
|
||||
import fr.dcproject.component.citizen.Citizen as CitizenEntity
|
||||
|
||||
class ForbiddenException(message: String) : Exception(message)
|
||||
|
||||
|
||||
@@ -3,7 +3,13 @@ package fr.dcproject
|
||||
import fr.dcproject.component.article.ArticleForView
|
||||
import fr.dcproject.component.article.ArticleRef
|
||||
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.Workgroup
|
||||
import io.ktor.features.*
|
||||
@@ -78,7 +84,7 @@ val converters: ConverterDeclaration = {
|
||||
decode { values, _ ->
|
||||
val id = values.singleOrNull()?.let { UUID.fromString(it) }
|
||||
?: 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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import com.rabbitmq.client.ConnectionFactory
|
||||
import fr.dcproject.component.article.ArticleRepository
|
||||
import fr.dcproject.component.article.ArticleViewManager
|
||||
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.messages.Mailer
|
||||
import fr.dcproject.messages.NotificationEmailSender
|
||||
@@ -27,7 +29,6 @@ import org.apache.http.HttpHost
|
||||
import org.elasticsearch.client.RestClient
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
import fr.dcproject.repository.Citizen as CitizenRepository
|
||||
import fr.dcproject.repository.CommentArticle as CommentArticleRepository
|
||||
import fr.dcproject.repository.CommentConstitution as CommentConstitutionRepository
|
||||
import fr.dcproject.repository.CommentGeneric as CommentGenericRepository
|
||||
@@ -117,6 +118,7 @@ val KoinModule = module {
|
||||
|
||||
// Voters
|
||||
single { ArticleVoter(get()) }
|
||||
single { CitizenVoter() }
|
||||
|
||||
// Elasticsearch Client
|
||||
single<RestClient> {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package fr.dcproject.component.article
|
||||
|
||||
import fr.dcproject.component.citizen.*
|
||||
import fr.dcproject.entity.*
|
||||
import fr.postgresjson.entity.*
|
||||
import org.joda.time.DateTime
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package fr.dcproject.component.article
|
||||
|
||||
import fr.dcproject.entity.CitizenI
|
||||
import fr.dcproject.component.citizen.CitizenI
|
||||
import fr.dcproject.entity.ViewAggregation
|
||||
import fr.dcproject.utils.contentToString
|
||||
import fr.dcproject.utils.getJsonField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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.VersionableRef
|
||||
import fr.dcproject.voter.Voter
|
||||
|
||||
@@ -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 org.joda.time.DateTime
|
||||
import java.util.*
|
||||
49
src/main/kotlin/component/citizen/CitizenRepository.kt
Normal file
49
src/main/kotlin/component/citizen/CitizenRepository.kt
Normal 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)
|
||||
}
|
||||
26
src/main/kotlin/component/citizen/CitizenVoter.kt
Normal file
26
src/main/kotlin/component/citizen/CitizenVoter.kt
Normal 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")
|
||||
}
|
||||
}
|
||||
45
src/main/kotlin/component/citizen/routes/ChangeMyPassword.kt
Normal file
45
src/main/kotlin/component/citizen/routes/ChangeMyPassword.kt
Normal 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/main/kotlin/component/citizen/routes/FindCitizens.kt
Normal file
33
src/main/kotlin/component/citizen/routes/FindCitizens.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/main/kotlin/component/citizen/routes/GetOneCitizen.kt
Normal file
23
src/main/kotlin/component/citizen/routes/GetOneCitizen.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package fr.dcproject.entity
|
||||
|
||||
import fr.dcproject.component.citizen.CitizenRef
|
||||
import fr.postgresjson.entity.*
|
||||
import org.joda.time.DateTime
|
||||
import java.util.*
|
||||
|
||||
@@ -2,6 +2,8 @@ package fr.dcproject.entity
|
||||
|
||||
import fr.dcproject.component.article.ArticleI
|
||||
import fr.dcproject.component.article.ArticleSimple
|
||||
import fr.dcproject.component.citizen.CitizenSimple
|
||||
import fr.dcproject.component.citizen.CitizenWithUserI
|
||||
import fr.postgresjson.entity.*
|
||||
import java.util.*
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package fr.dcproject.entity
|
||||
|
||||
import fr.dcproject.component.citizen.CitizenI
|
||||
import fr.postgresjson.entity.EntityCreatedBy
|
||||
import fr.postgresjson.entity.EntityI
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package fr.dcproject.entity
|
||||
|
||||
import fr.dcproject.component.article.ArticleRef
|
||||
import fr.dcproject.component.citizen.CitizenI
|
||||
import fr.postgresjson.entity.EntityCreatedAt
|
||||
import fr.postgresjson.entity.EntityCreatedBy
|
||||
import fr.postgresjson.entity.UuidEntity
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
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 java.util.*
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package fr.dcproject.entity
|
||||
|
||||
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 java.util.*
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
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 java.util.*
|
||||
@Deprecated("")
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
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.Role
|
||||
import fr.postgresjson.entity.*
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.rabbitmq.client.*
|
||||
import com.rabbitmq.client.BuiltinExchangeType.DIRECT
|
||||
import fr.dcproject.Config
|
||||
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.TargetRef
|
||||
import fr.dcproject.event.publisher.Publisher
|
||||
|
||||
@@ -5,13 +5,13 @@ import com.sendgrid.helpers.mail.objects.Content
|
||||
import com.sendgrid.helpers.mail.objects.Email
|
||||
import fr.dcproject.component.article.ArticleRepository
|
||||
import fr.dcproject.component.article.ArticleWithTitleI
|
||||
import fr.dcproject.entity.CitizenBasicI
|
||||
import fr.dcproject.entity.CitizenRef
|
||||
import fr.dcproject.component.citizen.CitizenBasicI
|
||||
import fr.dcproject.component.citizen.CitizenRef
|
||||
import fr.dcproject.component.citizen.CitizenRepository
|
||||
import fr.dcproject.entity.FollowSimple
|
||||
import fr.dcproject.entity.TargetRef
|
||||
import fr.postgresjson.entity.UuidEntityI
|
||||
import java.util.*
|
||||
import fr.dcproject.repository.Citizen as CitizenRepository
|
||||
|
||||
class NotificationEmailSender(
|
||||
private val mailer: Mailer,
|
||||
|
||||
@@ -4,9 +4,9 @@ import com.sendgrid.helpers.mail.Mail
|
||||
import com.sendgrid.helpers.mail.objects.Content
|
||||
import com.sendgrid.helpers.mail.objects.Email
|
||||
import fr.dcproject.JwtConfig
|
||||
import fr.dcproject.entity.CitizenBasicI
|
||||
import io.ktor.http.URLBuilder
|
||||
import fr.dcproject.repository.Citizen as CitizenRepository
|
||||
import fr.dcproject.component.citizen.CitizenBasicI
|
||||
import fr.dcproject.component.citizen.CitizenRepository
|
||||
import io.ktor.http.*
|
||||
|
||||
class SsoManager(
|
||||
private val mailer: Mailer,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package fr.dcproject.repository
|
||||
|
||||
import fr.dcproject.component.article.ArticleForView
|
||||
import fr.dcproject.component.article.ArticleRef
|
||||
import fr.dcproject.component.citizen.CitizenI
|
||||
import fr.dcproject.component.citizen.CitizenRef
|
||||
import fr.dcproject.entity.*
|
||||
import fr.postgresjson.connexion.Paginated
|
||||
import fr.postgresjson.connexion.Requester
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package fr.dcproject.repository
|
||||
|
||||
import fr.dcproject.component.article.ArticleRef
|
||||
import fr.dcproject.entity.CitizenWithUserI
|
||||
import fr.dcproject.component.citizen.CitizenWithUserI
|
||||
import fr.dcproject.entity.ConstitutionSimple
|
||||
import fr.postgresjson.connexion.Paginated
|
||||
import fr.postgresjson.connexion.Requester
|
||||
|
||||
@@ -2,7 +2,12 @@ package fr.dcproject.repository
|
||||
|
||||
import fr.dcproject.component.article.ArticleForView
|
||||
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.Requester
|
||||
import fr.postgresjson.entity.UuidEntity
|
||||
|
||||
@@ -2,7 +2,7 @@ package fr.dcproject.repository
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
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.OpinionForUpdate
|
||||
import fr.dcproject.entity.TargetRef
|
||||
@@ -11,7 +11,7 @@ import fr.postgresjson.connexion.Requester
|
||||
import fr.postgresjson.repository.RepositoryI
|
||||
import net.pearx.kasechange.toSnakeCase
|
||||
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.OpinionArticle as OpinionArticleEntity
|
||||
import fr.dcproject.entity.OpinionChoice as OpinionChoiceEntity
|
||||
|
||||
@@ -2,13 +2,14 @@ package fr.dcproject.repository
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
import fr.dcproject.component.article.ArticleForView
|
||||
import fr.dcproject.component.citizen.CitizenRef
|
||||
import fr.dcproject.entity.*
|
||||
import fr.dcproject.entity.Constitution
|
||||
import fr.postgresjson.connexion.Paginated
|
||||
import fr.postgresjson.connexion.Requester
|
||||
import fr.postgresjson.repository.RepositoryI
|
||||
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
|
||||
|
||||
open class Vote<T : TargetI>(override var requester: Requester) : RepositoryI {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
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.postgresjson.connexion.Paginated
|
||||
import fr.postgresjson.connexion.Requester
|
||||
|
||||
@@ -3,25 +3,22 @@ package fr.dcproject.routes
|
||||
import com.fasterxml.jackson.databind.exc.MismatchedInputException
|
||||
import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
|
||||
import fr.dcproject.JwtConfig
|
||||
import fr.dcproject.component.citizen.CitizenRepository
|
||||
import fr.dcproject.entity.UserI.Roles.ROLE_USER
|
||||
import fr.dcproject.messages.SsoManager
|
||||
import fr.dcproject.routes.AuthPaths.LoginRequest
|
||||
import fr.dcproject.routes.AuthPaths.RegisterRequest
|
||||
import fr.dcproject.routes.AuthPaths.SsoRequest
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.UserPasswordCredential
|
||||
import io.ktor.features.BadRequestException
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.locations.Location
|
||||
import io.ktor.locations.post
|
||||
import io.ktor.request.receive
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.response.respondText
|
||||
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 io.ktor.application.*
|
||||
import io.ktor.auth.*
|
||||
import io.ktor.features.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.locations.*
|
||||
import io.ktor.request.*
|
||||
import io.ktor.response.*
|
||||
import io.ktor.routing.*
|
||||
import io.ktor.util.*
|
||||
import fr.dcproject.component.citizen.Citizen as CitizenEntity
|
||||
import fr.dcproject.repository.User as UserRepository
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package fr.dcproject.routes
|
||||
import fr.dcproject.citizen
|
||||
import fr.dcproject.component.article.ArticleForView
|
||||
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.repository.CommentArticle.Sort
|
||||
import fr.dcproject.security.voter.CommentVoter.Action.CREATE
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package fr.dcproject.routes
|
||||
|
||||
import fr.dcproject.citizen
|
||||
import fr.dcproject.entity.Citizen
|
||||
import fr.dcproject.component.citizen.Citizen
|
||||
import fr.dcproject.entity.CommentForUpdate
|
||||
import fr.dcproject.entity.ConstitutionRef
|
||||
import fr.dcproject.security.voter.CommentVoter.Action.CREATE
|
||||
|
||||
@@ -2,7 +2,7 @@ package fr.dcproject.routes
|
||||
|
||||
import fr.dcproject.citizen
|
||||
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.TitleSimple
|
||||
import fr.dcproject.security.voter.ConstitutionVoter.Action.CREATE
|
||||
|
||||
@@ -2,7 +2,7 @@ package fr.dcproject.routes
|
||||
|
||||
import fr.dcproject.citizen
|
||||
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.security.voter.FollowVoter.Action.*
|
||||
import fr.ktorVoter.assertCan
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package fr.dcproject.routes
|
||||
|
||||
import fr.dcproject.citizen
|
||||
import fr.dcproject.entity.CitizenRef
|
||||
import fr.dcproject.component.citizen.CitizenRef
|
||||
import fr.dcproject.entity.ConstitutionRef
|
||||
import fr.dcproject.entity.FollowForUpdate
|
||||
import fr.dcproject.security.voter.FollowVoter.Action.*
|
||||
|
||||
@@ -2,7 +2,7 @@ package fr.dcproject.routes
|
||||
|
||||
import fr.dcproject.citizen
|
||||
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.security.voter.OpinionVoter.Action.CREATE
|
||||
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.get
|
||||
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
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
|
||||
@@ -2,7 +2,7 @@ package fr.dcproject.routes
|
||||
|
||||
import fr.dcproject.citizen
|
||||
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.repository.CommentGeneric
|
||||
import fr.dcproject.repository.VoteComment
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package fr.dcproject.routes
|
||||
|
||||
import fr.dcproject.citizen
|
||||
import fr.dcproject.entity.Citizen
|
||||
import fr.dcproject.component.citizen.Citizen
|
||||
import fr.dcproject.entity.VoteForUpdate
|
||||
import fr.dcproject.routes.VoteConstitutionPaths.ConstitutionVoteRequest.Content
|
||||
import fr.dcproject.security.voter.VoteVoter.Action.CREATE
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package fr.dcproject.routes
|
||||
|
||||
import fr.dcproject.citizen
|
||||
import fr.dcproject.entity.CitizenRef
|
||||
import fr.dcproject.component.citizen.CitizenRef
|
||||
import fr.dcproject.entity.WorkgroupSimple
|
||||
import fr.dcproject.entity.WorkgroupWithMembersI.Member
|
||||
import fr.dcproject.entity.WorkgroupWithMembersI.Member.Role
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package fr.dcproject.views
|
||||
|
||||
import fr.dcproject.entity.CitizenI
|
||||
import fr.dcproject.component.citizen.CitizenI
|
||||
import fr.dcproject.entity.ViewAggregation
|
||||
import org.elasticsearch.client.Response
|
||||
import org.joda.time.DateTime
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package fr.dcproject.security.voter
|
||||
|
||||
import fr.dcproject.citizenOrNull
|
||||
import fr.dcproject.entity.CitizenI
|
||||
import fr.dcproject.component.citizen.CitizenI
|
||||
import fr.dcproject.entity.FollowI
|
||||
import fr.dcproject.voter.NoSubjectDefinedException
|
||||
import fr.ktorVoter.*
|
||||
|
||||
Reference in New Issue
Block a user