Big refactoring #77

Merged
flecomte merged 166 commits from refactoring-component-and-immutable into master 2021-03-24 19:06:07 +01:00
17 changed files with 154 additions and 286 deletions
Showing only changes of commit c380ba47a5 - Show all commits

View File

@@ -28,7 +28,6 @@ import fr.dcproject.component.comment.generic.routes.createCommentChildren
import fr.dcproject.component.comment.generic.routes.editComment import fr.dcproject.component.comment.generic.routes.editComment
import fr.dcproject.component.comment.generic.routes.getChildrenComments import fr.dcproject.component.comment.generic.routes.getChildrenComments
import fr.dcproject.component.comment.generic.routes.getOneComment import fr.dcproject.component.comment.generic.routes.getOneComment
import fr.dcproject.component.workgroup.routes.*
import fr.dcproject.component.workgroup.routes.CreateWorkgroup.createWorkgroup import fr.dcproject.component.workgroup.routes.CreateWorkgroup.createWorkgroup
import fr.dcproject.component.workgroup.routes.DeleteWorkgroup.deleteWorkgroup import fr.dcproject.component.workgroup.routes.DeleteWorkgroup.deleteWorkgroup
import fr.dcproject.component.workgroup.routes.EditWorkgroup.editWorkgroup import fr.dcproject.component.workgroup.routes.EditWorkgroup.editWorkgroup
@@ -97,8 +96,7 @@ fun Application.module(env: Env = PROD) {
VoteVoter(), VoteVoter(),
FollowVoter(), FollowVoter(),
OpinionVoter(), OpinionVoter(),
OpinionChoiceVoter(), OpinionChoiceVoter()
WorkgroupVoter()
) )
} }
@@ -196,15 +194,15 @@ fun Application.module(env: Env = PROD) {
authRegister(get()) authRegister(get())
authSso(get()) authSso(get())
/* Workgroup */ /* Workgroup */
getWorkgroups(get()) getWorkgroups(get(), get())
getWorkgroup(get()) getWorkgroup(get(), get())
createWorkgroup(get()) createWorkgroup(get(), get())
editWorkgroup(get()) editWorkgroup(get(), get())
deleteWorkgroup(get()) deleteWorkgroup(get(), get())
/* Workgroup members */ /* Workgroup members */
addMemberToWorkgroup(get()) addMemberToWorkgroup(get(), get())
deleteMemberOfWorkgroup(get()) deleteMemberOfWorkgroup(get(), get())
updateMemberOfWorkgroup(get()) updateMemberOfWorkgroup(get(), get())
/* TODO */ /* TODO */
constitution(get()) constitution(get())
followArticle(get()) followArticle(get())

View File

@@ -18,6 +18,7 @@ import fr.dcproject.component.citizen.CitizenVoter
import fr.dcproject.component.comment.article.CommentArticleRepository import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.dcproject.component.comment.generic.CommentVoter import fr.dcproject.component.comment.generic.CommentVoter
import fr.dcproject.component.workgroup.WorkgroupRepository import fr.dcproject.component.workgroup.WorkgroupRepository
import fr.dcproject.component.workgroup.WorkgroupVoter
import fr.dcproject.event.publisher.Publisher import fr.dcproject.event.publisher.Publisher
import fr.dcproject.messages.Mailer import fr.dcproject.messages.Mailer
import fr.dcproject.messages.NotificationEmailSender import fr.dcproject.messages.NotificationEmailSender
@@ -121,6 +122,7 @@ val KoinModule = module {
single { ArticleVoter(get()) } single { ArticleVoter(get()) }
single { CitizenVoter() } single { CitizenVoter() }
single { CommentVoter() } single { CommentVoter() }
single { WorkgroupVoter() }
// Elasticsearch Client // Elasticsearch Client
single<RestClient> { single<RestClient> {

View File

@@ -59,7 +59,7 @@ interface WorkgroupWithAuthI<Z : CitizenWithUserI> : WorkgroupWithMembersI<Z>, E
val anonymous: Boolean val anonymous: Boolean
fun isMember(user: UserI): Boolean = members.isMember(user) fun isMember(user: UserI): Boolean = members.isMember(user)
fun isMember(citizen: CitizenWithUserI): Boolean = members.isMember(citizen) fun isMember(citizen: CitizenI): Boolean = members.isMember(citizen)
fun hasRole(expectedRole: Role, user: UserI): Boolean = members.hasRole(expectedRole, user) fun hasRole(expectedRole: Role, user: UserI): Boolean = members.hasRole(expectedRole, user)
fun hasRole(expectedRole: Role, citizen: CitizenI): Boolean = members.hasRole(expectedRole, citizen) fun hasRole(expectedRole: Role, citizen: CitizenI): Boolean = members.hasRole(expectedRole, citizen)

View File

@@ -0,0 +1,51 @@
package fr.dcproject.component.workgroup
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.workgroup.WorkgroupWithMembersI.Member.Role
import fr.dcproject.voter.Voter
import fr.dcproject.voter.VoterResponse
class WorkgroupVoter : Voter() {
fun canCreate(subject: WorkgroupI, citizen: CitizenI?): VoterResponse {
if (citizen == null) return denied("You must be connected to create workgroup", "workgroup.create.notConnected")
return granted()
}
fun <S : WorkgroupWithAuthI<*>> canView(subjects: List<S>, citizen: CitizenI?): VoterResponse =
canAll(subjects) { canView(it, citizen) }
fun canView(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): VoterResponse =
if (subject.isDeleted()) denied("You cannot view a deleted workgroup", "workgroup.view.deleted")
else if (!subject.anonymous) granted()
else if (subject.anonymous && citizen != null && subject.isMember(citizen)) granted()
else denied("You cannot view anonymous workgroup", "workgroup.view.anonymous")
fun canDelete(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): VoterResponse {
if (citizen == null) return denied("You must be connected to delete workgroup", "workgroup.delete.notConnected")
return if (subject.hasRole(Role.MASTER, citizen)) granted()
else denied("You must hase role MASTER to delete workgroup", "workgroup.delete.role")
}
fun canUpdate(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): VoterResponse {
if (citizen == null) return denied("You must be connected to update workgroup", "workgroup.update.notConnected")
return if (subject.hasRole(Role.MASTER, citizen)) granted()
else denied("You must hase role MASTER to delete workgroup", "workgroup.delete.role")
}
fun canAddMembers(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): VoterResponse = when {
citizen == null -> denied("You must be connected to add member to the workgroup", "workgroup.addMember.notConnected")
subject.hasRole(Role.MASTER, citizen) -> granted()
else -> denied("You must have MASTER Role for add member to workgroup", "workgroup.addMember.role")
}
fun canUpdateMembers(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): VoterResponse = when {
citizen == null -> denied("You must be connected to update member of the workgroup", "workgroup.updateMember.notConnected")
subject.hasRole(Role.MASTER, citizen) -> granted()
else -> denied("You must have MASTER Role for update members of workgroup", "workgroup.updateMember.role")
}
fun canRemoveMembers(subject: WorkgroupWithAuthI<*>, citizen: CitizenI?): VoterResponse = when {
citizen == null -> denied("You must be connected to remove member of the workgroup", "workgroup.removeMember.notConnected")
subject.hasRole(Role.MASTER, citizen) -> granted()
else -> denied("You must have MASTER Role for remove members of workgroup", "workgroup.removeMember.role")
}
}

View File

@@ -1,11 +1,12 @@
package fr.dcproject.component.workgroup.routes package fr.dcproject.component.workgroup.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.citizenOrNull
import fr.dcproject.component.workgroup.WorkgroupRepository import fr.dcproject.component.workgroup.WorkgroupRepository
import fr.dcproject.component.workgroup.WorkgroupSimple import fr.dcproject.component.workgroup.WorkgroupSimple
import fr.dcproject.component.workgroup.routes.CreateWorkgroup.PostWorkgroupRequest.Input import fr.dcproject.component.workgroup.routes.CreateWorkgroup.PostWorkgroupRequest.Input
import fr.dcproject.security.voter.WorkgroupVoter import fr.dcproject.component.workgroup.WorkgroupVoter
import fr.ktorVoter.assertCan import fr.dcproject.voter.assert
import io.ktor.application.* import io.ktor.application.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.locations.* import io.ktor.locations.*
@@ -27,7 +28,7 @@ object CreateWorkgroup {
) )
} }
fun Route.createWorkgroup(repo: WorkgroupRepository) { fun Route.createWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
post<PostWorkgroupRequest> { post<PostWorkgroupRequest> {
call.receive<Input>().run { call.receive<Input>().run {
WorkgroupSimple( WorkgroupSimple(
@@ -39,7 +40,7 @@ object CreateWorkgroup {
citizen citizen
) )
}.let { workgroup -> }.let { workgroup ->
assertCan(WorkgroupVoter.Action.CREATE, workgroup) voter.assert { canCreate(workgroup, citizenOrNull) }
repo.upsert(workgroup) repo.upsert(workgroup)
}.let { }.let {
call.respond(HttpStatusCode.Created, it) call.respond(HttpStatusCode.Created, it)

View File

@@ -1,8 +1,9 @@
package fr.dcproject.component.workgroup.routes package fr.dcproject.component.workgroup.routes
import fr.dcproject.citizenOrNull
import fr.dcproject.component.workgroup.WorkgroupRepository import fr.dcproject.component.workgroup.WorkgroupRepository
import fr.dcproject.security.voter.WorkgroupVoter import fr.dcproject.component.workgroup.WorkgroupVoter
import fr.ktorVoter.assertCan import fr.dcproject.voter.assert
import io.ktor.application.* import io.ktor.application.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.locations.* import io.ktor.locations.*
@@ -15,10 +16,10 @@ object DeleteWorkgroup {
@Location("/workgroups/{workgroupId}") @Location("/workgroups/{workgroupId}")
class DeleteWorkgroupRequest(val workgroupId: UUID) class DeleteWorkgroupRequest(val workgroupId: UUID)
fun Route.deleteWorkgroup(repo: WorkgroupRepository) { fun Route.deleteWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
delete<DeleteWorkgroupRequest> { delete<DeleteWorkgroupRequest> {
repo.findById(it.workgroupId)?.let { workgroup -> repo.findById(it.workgroupId)?.let { workgroup ->
assertCan(WorkgroupVoter.Action.DELETE, workgroup) voter.assert { canDelete(workgroup, citizenOrNull) }
repo.delete(workgroup) repo.delete(workgroup)
call.respond(HttpStatusCode.NoContent) call.respond(HttpStatusCode.NoContent)
} ?: call.respond(HttpStatusCode.NotFound) } ?: call.respond(HttpStatusCode.NotFound)

View File

@@ -1,9 +1,10 @@
package fr.dcproject.component.workgroup.routes package fr.dcproject.component.workgroup.routes
import fr.dcproject.citizenOrNull
import fr.dcproject.component.workgroup.WorkgroupRepository import fr.dcproject.component.workgroup.WorkgroupRepository
import fr.dcproject.component.workgroup.routes.EditWorkgroup.PutWorkgroupRequest.Input import fr.dcproject.component.workgroup.routes.EditWorkgroup.PutWorkgroupRequest.Input
import fr.dcproject.security.voter.WorkgroupVoter import fr.dcproject.component.workgroup.WorkgroupVoter
import fr.ktorVoter.assertCan import fr.dcproject.voter.assert
import io.ktor.application.* import io.ktor.application.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.locations.* import io.ktor.locations.*
@@ -25,7 +26,7 @@ object EditWorkgroup {
) )
} }
fun Route.editWorkgroup(repo: WorkgroupRepository) { fun Route.editWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
put<PutWorkgroupRequest> { put<PutWorkgroupRequest> {
repo.findById(it.workgroupId)?.let { old -> repo.findById(it.workgroupId)?.let { old ->
call.receive<Input>().run { call.receive<Input>().run {
@@ -35,7 +36,7 @@ object EditWorkgroup {
logo = logo ?: old.logo, logo = logo ?: old.logo,
anonymous = anonymous ?: old.anonymous anonymous = anonymous ?: old.anonymous
).let { workgroup -> ).let { workgroup ->
assertCan(WorkgroupVoter.Action.UPDATE, workgroup) voter.assert { canUpdate(workgroup, citizenOrNull) }
repo.upsert(workgroup) repo.upsert(workgroup)
call.respond(HttpStatusCode.OK, it) call.respond(HttpStatusCode.OK, it)
} }

View File

@@ -1,8 +1,9 @@
package fr.dcproject.component.workgroup.routes package fr.dcproject.component.workgroup.routes
import fr.dcproject.citizenOrNull
import fr.dcproject.component.workgroup.WorkgroupRepository import fr.dcproject.component.workgroup.WorkgroupRepository
import fr.dcproject.security.voter.WorkgroupVoter import fr.dcproject.component.workgroup.WorkgroupVoter
import fr.ktorVoter.assertCan import fr.dcproject.voter.assert
import io.ktor.application.* import io.ktor.application.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.locations.* import io.ktor.locations.*
@@ -15,10 +16,10 @@ object GetWorkgroup {
@Location("/workgroups/{workgroupId}") @Location("/workgroups/{workgroupId}")
class WorkgroupRequest(val workgroupId: UUID) class WorkgroupRequest(val workgroupId: UUID)
fun Route.getWorkgroup(repo: WorkgroupRepository) { fun Route.getWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
get<WorkgroupRequest> { get<WorkgroupRequest> {
repo.findById(it.workgroupId)?.let { workgroup -> repo.findById(it.workgroupId)?.let { workgroup ->
assertCan(WorkgroupVoter.Action.VIEW, workgroup) voter.assert { canView(workgroup, citizenOrNull) }
call.respond(workgroup) call.respond(workgroup)
} ?: call.respond(HttpStatusCode.NotFound) } ?: call.respond(HttpStatusCode.NotFound)
} }

View File

@@ -1,9 +1,10 @@
package fr.dcproject.component.workgroup.routes package fr.dcproject.component.workgroup.routes
import fr.dcproject.citizenOrNull
import fr.dcproject.component.workgroup.WorkgroupRepository import fr.dcproject.component.workgroup.WorkgroupRepository
import fr.dcproject.security.voter.WorkgroupVoter import fr.dcproject.component.workgroup.WorkgroupVoter
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
import fr.ktorVoter.assertCanAll import fr.dcproject.voter.assert
import fr.postgresjson.repository.RepositoryI import fr.postgresjson.repository.RepositoryI
import io.ktor.application.* import io.ktor.application.*
import io.ktor.locations.* import io.ktor.locations.*
@@ -28,13 +29,13 @@ object GetWorkgroups {
val members: List<UUID>? = members?.toUUID() val members: List<UUID>? = members?.toUUID()
} }
fun Route.getWorkgroups(repo: WorkgroupRepository) { fun Route.getWorkgroups(repo: WorkgroupRepository, voter: WorkgroupVoter) {
get<WorkgroupsRequest> { get<WorkgroupsRequest> {
val workgroups = val workgroups =
repo.find(it.page, it.limit, it.sort, it.direction, it.search, repo.find(it.page, it.limit, it.sort, it.direction, it.search,
WorkgroupRepository.Filter(createdById = it.createdBy, members = it.members) WorkgroupRepository.Filter(createdById = it.createdBy, members = it.members)
) )
assertCanAll(WorkgroupVoter.Action.VIEW, workgroups.result) voter.assert { canView(workgroups.result, citizenOrNull) }
call.respond(workgroups) call.respond(workgroups)
} }
} }

View File

@@ -1,10 +1,11 @@
package fr.dcproject.component.workgroup.routes.members package fr.dcproject.component.workgroup.routes.members
import fr.dcproject.citizenOrNull
import fr.dcproject.component.citizen.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.workgroup.WorkgroupRepository import fr.dcproject.component.workgroup.WorkgroupRepository
import fr.dcproject.component.workgroup.WorkgroupWithMembersI import fr.dcproject.component.workgroup.WorkgroupWithMembersI
import fr.dcproject.security.voter.WorkgroupVoter import fr.dcproject.component.workgroup.WorkgroupVoter
import fr.ktorVoter.assertCan import fr.dcproject.voter.assert
import io.ktor.application.* import io.ktor.application.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.locations.* import io.ktor.locations.*
@@ -36,12 +37,12 @@ object AddMemberToWorkgroup {
} }
@KtorExperimentalLocationsAPI @KtorExperimentalLocationsAPI
fun Route.addMemberToWorkgroup(repo: WorkgroupRepository) { fun Route.addMemberToWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
/* Add members to workgroup */ /* Add members to workgroup */
post<WorkgroupsMembersRequest> { post<WorkgroupsMembersRequest> {
repo.findById(it.workgroupId)?.let { workgroup -> repo.findById(it.workgroupId)?.let { workgroup ->
call.getMembersFromRequest().let { members -> call.getMembersFromRequest().let { members ->
assertCan(WorkgroupVoter.ActionMembers.ADD, workgroup) voter.assert { canAddMembers(workgroup, citizenOrNull) }
repo.addMembers(workgroup, members) repo.addMembers(workgroup, members)
}.let { members -> }.let { members ->
call.respond(HttpStatusCode.Created, members) call.respond(HttpStatusCode.Created, members)

View File

@@ -1,10 +1,11 @@
package fr.dcproject.component.workgroup.routes.members package fr.dcproject.component.workgroup.routes.members
import fr.dcproject.citizenOrNull
import fr.dcproject.component.citizen.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.workgroup.WorkgroupRepository import fr.dcproject.component.workgroup.WorkgroupRepository
import fr.dcproject.component.workgroup.WorkgroupWithMembersI import fr.dcproject.component.workgroup.WorkgroupWithMembersI
import fr.dcproject.security.voter.WorkgroupVoter import fr.dcproject.component.workgroup.WorkgroupVoter
import fr.ktorVoter.assertCan import fr.dcproject.voter.assert
import io.ktor.application.* import io.ktor.application.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.locations.* import io.ktor.locations.*
@@ -34,12 +35,12 @@ object DeleteMembersOfWorkgroup {
) )
} }
fun Route.deleteMemberOfWorkgroup(repo: WorkgroupRepository) { fun Route.deleteMemberOfWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
/* Delete members of workgroup */ /* Delete members of workgroup */
delete<WorkgroupsMembersRequest> { delete<WorkgroupsMembersRequest> {
repo.findById(it.workgroupId)?.let { workgroup -> repo.findById(it.workgroupId)?.let { workgroup ->
call.getMembersFromRequest().let { members -> call.getMembersFromRequest().let { members ->
assertCan(WorkgroupVoter.ActionMembers.REMOVE, workgroup) voter.assert { canView(workgroup, citizenOrNull) }
repo.removeMembers(workgroup, members) repo.removeMembers(workgroup, members)
}.let { members -> }.let { members ->
call.respond(HttpStatusCode.OK, members) call.respond(HttpStatusCode.OK, members)

View File

@@ -1,10 +1,11 @@
package fr.dcproject.component.workgroup.routes.members package fr.dcproject.component.workgroup.routes.members
import fr.dcproject.citizenOrNull
import fr.dcproject.component.citizen.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.workgroup.WorkgroupRepository import fr.dcproject.component.workgroup.WorkgroupRepository
import fr.dcproject.component.workgroup.WorkgroupWithMembersI import fr.dcproject.component.workgroup.WorkgroupWithMembersI
import fr.dcproject.security.voter.WorkgroupVoter import fr.dcproject.component.workgroup.WorkgroupVoter
import fr.ktorVoter.assertCan import fr.dcproject.voter.assert
import io.ktor.application.* import io.ktor.application.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.locations.* import io.ktor.locations.*
@@ -34,12 +35,12 @@ object UpdateMemberOfWorkgroup {
) )
} }
fun Route.updateMemberOfWorkgroup(repo: WorkgroupRepository) { fun Route.updateMemberOfWorkgroup(repo: WorkgroupRepository, voter: WorkgroupVoter) {
/* Update members of workgroup */ /* Update members of workgroup */
put<WorkgroupsMembersRequest> { put<WorkgroupsMembersRequest> {
repo.findById(it.workgroupId)?.let { workgroup -> repo.findById(it.workgroupId)?.let { workgroup ->
call.getMembersFromRequest().let { members -> call.getMembersFromRequest().let { members ->
assertCan(WorkgroupVoter.ActionMembers.UPDATE, workgroup) voter.assert { canUpdateMembers(workgroup, citizenOrNull) }
repo.updateMembers(workgroup, members) repo.updateMembers(workgroup, members)
}.let { members -> }.let { members ->
call.respond(HttpStatusCode.OK, members) call.respond(HttpStatusCode.OK, members)

View File

@@ -1,95 +0,0 @@
package fr.dcproject.security.voter
import fr.dcproject.component.auth.UserI
import fr.dcproject.component.workgroup.WorkgroupI
import fr.dcproject.component.workgroup.WorkgroupWithAuthI
import fr.dcproject.component.workgroup.WorkgroupWithMembersI.Member.Role
import fr.dcproject.user
import fr.dcproject.voter.NoRuleDefinedException
import fr.dcproject.voter.NoSubjectDefinedException
import fr.ktorVoter.*
import io.ktor.application.*
class WorkgroupVoter : Voter<ApplicationCall> {
enum class Action : ActionI {
CREATE,
UPDATE,
VIEW,
DELETE,
}
enum class ActionMembers : ActionI {
ADD,
UPDATE,
VIEW,
REMOVE,
}
override fun invoke(action: Any, context: ApplicationCall, subject: Any?): VoterResponseI {
if ((action is Action && subject == null)) throw NoSubjectDefinedException(action)
if (!((action is Action || action is ActionMembers) &&
(subject is WorkgroupI? || (subject is List<*> && subject.first() is WorkgroupI)))) return abstain()
val user = context.user
if (action == Action.CREATE) {
if (user == null) return denied("You must be connected to delete workgroup", "workgroup.delete.notConnected")
if (subject is WorkgroupI) {
return granted()
}
}
if (action == Action.VIEW) {
if (subject is WorkgroupWithAuthI<*>) {
return if (subject.isDeleted()) denied("You cannot view a deleted workgroup", "workgroup.view.deleted")
else if (!subject.anonymous) granted()
else if (subject.anonymous && user != null && subject.isMember(user)) granted()
else denied("You cannot view anonymous workgroup", "workgroup.view.anonymous")
}
throw NoSubjectDefinedException(action as ActionI)
}
if (subject is WorkgroupWithAuthI<*> && (action == Action.DELETE || action == Action.UPDATE)) {
if (action == Action.DELETE) {
if (user == null) return denied("You must be connected to delete workgroup", "workgroup.delete.notConnected")
return if (subject.hasRole(Role.MASTER, user)) granted()
else denied("You must hase role MASTER to delete workgroup", "workgroup.delete.role")
}
if (action == Action.UPDATE) {
if (user == null) return denied("You must be connected to delete workgroup", "workgroup.delete.notConnected")
return if (subject.hasRole(Role.MASTER, user)) granted()
else denied("You must hase role MASTER to delete workgroup", "workgroup.delete.role")
}
throw NoRuleDefinedException(action as ActionI)
} else if (subject !is WorkgroupWithAuthI<*> && (action == Action.DELETE || action == Action.UPDATE)) {
throw NoSubjectDefinedException(action as ActionI)
}
if (action == ActionMembers.ADD) {
// TODO create ROLES
if (user !is UserI) return denied("You must be connected to add member to the workgroup", "workgroup.addMember.notConnected")
if (subject !is WorkgroupWithAuthI<*>) throw NoSubjectDefinedException(action as ActionI)
return if (subject.hasRole(Role.MASTER, user)) granted() else denied("You must have MASTER Role for add member to workgroup", "workgroup.addMember.role")
}
if (action == ActionMembers.UPDATE) {
// TODO create ROLES
if (user !is UserI) return denied("You must be connected to update member of the workgroup", "workgroup.updateMember.notConnected")
if (subject !is WorkgroupWithAuthI<*>) throw NoSubjectDefinedException(action as ActionI)
return if (subject.hasRole(Role.MASTER, user)) granted() else denied("You must have MASTER Role for update members of workgroup", "workgroup.updateMember.role")
}
if (action == ActionMembers.REMOVE) {
// TODO create ROLES
if (user !is UserI) return denied("You must be connected to remove member of the workgroup", "workgroup.removeMember.notConnected")
if (subject !is WorkgroupWithAuthI<*>) throw NoSubjectDefinedException(action as ActionI)
return if (subject.hasRole(Role.MASTER, user)) granted() else denied("You must have MASTER Role for remove members of workgroup", "workgroup.removeMember.role")
}
if (action is Action) {
throw NoRuleDefinedException(action)
}
return abstain()
}
}

View File

@@ -11,7 +11,6 @@ import fr.dcproject.voter.Vote.GRANTED
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
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
@@ -48,10 +47,6 @@ internal class ArticleVoterTest {
} }
} }
init {
mockkStatic("fr.dcproject.ApplicationContextKt")
}
@Test @Test
fun `creator can be view the article`() { fun `creator can be view the article`() {
val article = getArticle(tesla).copy(draft = true) val article = getArticle(tesla).copy(draft = true)

View File

@@ -7,7 +7,6 @@ import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenVoter import fr.dcproject.component.citizen.CitizenVoter
import fr.dcproject.voter.Vote.DENIED import fr.dcproject.voter.Vote.DENIED
import fr.dcproject.voter.Vote.GRANTED import fr.dcproject.voter.Vote.GRANTED
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
@@ -50,10 +49,6 @@ internal class CitizenVoterTest {
deletedAt = DateTime.now() deletedAt = DateTime.now()
) )
init {
mockkStatic("fr.dcproject.ApplicationContextKt")
}
@Test @Test
fun `can be view the citizen`() { fun `can be view the citizen`() {
CitizenVoter() CitizenVoter()

View File

@@ -15,7 +15,6 @@ import fr.dcproject.voter.Vote.GRANTED
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
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
@@ -106,10 +105,6 @@ internal class CommentVoterTest {
every { findVersionsByVersionId(1, 1, any()) } returns Paginated(listOf(article1), 0, 1, 1) every { findVersionsByVersionId(1, 1, any()) } returns Paginated(listOf(article1), 0, 1, 1)
} }
init {
mockkStatic("fr.dcproject.ApplicationContextKt")
}
@Test @Test
fun `can be view the comment`() { fun `can be view the comment`() {
CommentVoter() CommentVoter()

View File

@@ -1,27 +1,19 @@
package unit.voter package unit.voter
import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.auth.User import fr.dcproject.component.auth.User
import fr.dcproject.component.auth.UserI import fr.dcproject.component.auth.UserI
import fr.dcproject.component.citizen.CitizenBasic import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenCart import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.workgroup.WorkgroupRef import fr.dcproject.component.workgroup.WorkgroupVoter
import fr.dcproject.component.workgroup.WorkgroupWithMembersI import fr.dcproject.component.workgroup.WorkgroupWithMembersI
import fr.dcproject.security.voter.WorkgroupVoter import fr.dcproject.voter.Vote.DENIED
import fr.dcproject.user import fr.dcproject.voter.Vote.GRANTED
import fr.dcproject.voter.NoSubjectDefinedException
import fr.ktorVoter.ActionI
import fr.ktorVoter.Vote
import fr.ktorVoter.VoterException
import fr.ktorVoter.can
import io.ktor.application.*
import io.mockk.every
import io.mockk.mockk
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.* import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.parallel.Execution import org.junit.jupiter.api.parallel.Execution
import org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT import org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT
import java.util.* import java.util.*
@@ -63,13 +55,6 @@ internal class WorkgroupVoterTest {
name = CitizenI.Name("Albert", "Einstein") name = CitizenI.Name("Albert", "Einstein")
) )
private val article1 = ArticleForView(
content = "Hi",
createdBy = einstein2,
description = "blablabla",
title = "Super article"
)
private val workgroupPublic = WorkgroupEntity( private val workgroupPublic = WorkgroupEntity(
createdBy = tesla, createdBy = tesla,
description = "Super desc", description = "Super desc",
@@ -86,146 +71,80 @@ internal class WorkgroupVoterTest {
anonymous = true anonymous = true
) )
private val workgroupref = WorkgroupRef() @Test
fun `can be view your workgroup`() {
init { WorkgroupVoter()
mockkStatic("fr.dcproject.ApplicationContextKt") .canView(workgroupPublic, tesla)
.vote `should be` GRANTED
} }
@Test @Test
fun `support workgroup`(): Unit = WorkgroupVoter().run { fun `can be view your workgroup if is not public`() {
val p = object : ActionI {} WorkgroupVoter()
mockk<ApplicationCall> { .canView(workgroupAnon, tesla)
every { user } returns tesla.user .vote `should be` GRANTED
}.let {
this(WorkgroupVoter.Action.VIEW, it, workgroupPublic).vote `should be` Vote.GRANTED
this(WorkgroupVoter.Action.VIEW, it, article1).vote `should be` Vote.ABSTAIN
this(p, it, workgroupPublic).vote `should be` Vote.ABSTAIN
}
} }
@Test @Test
fun `can be view your workgroup`(): Unit = listOf(WorkgroupVoter()).run { fun `can be view workgroup of other if is public`() {
mockk<ApplicationCall> { WorkgroupVoter()
every { user } returns tesla.user .canView(workgroupPublic, einstein)
}.let { .vote `should be` GRANTED
can(WorkgroupVoter.Action.VIEW, it, workgroupPublic) `should be` true
}
} }
@Test @Test
fun `can be view your workgroup if is not public`(): Unit = listOf(WorkgroupVoter()).run { fun `can not be view workgroup of other if is not public`() {
mockk<ApplicationCall> { WorkgroupVoter()
every { user } returns tesla.user .canView(workgroupAnon, einstein)
}.let { .vote `should be` DENIED
can(WorkgroupVoter.Action.VIEW, it, workgroupAnon) `should be` true
}
} }
@Test @Test
fun `can be view workgroup of other if is public`(): Unit = listOf(WorkgroupVoter()).run { fun `can be view your workgroup list`() {
mockk<ApplicationCall> { WorkgroupVoter()
every { user } returns einstein.user .canView(listOf(workgroupPublic, workgroupAnon), tesla)
}.let { .vote `should be` GRANTED
can(WorkgroupVoter.Action.VIEW, it, workgroupPublic) `should be` true
}
} }
@Test @Test
fun `can not be view workgroup of other if is not public`(): Unit = listOf(WorkgroupVoter()).run { fun `can be create workgroup`() {
mockk<ApplicationCall> { WorkgroupVoter()
every { user } returns einstein.user .canCreate(workgroupPublic, tesla)
}.let { .vote `should be` GRANTED
can(WorkgroupVoter.Action.VIEW, it, workgroupAnon) `should be` false
}
} }
@Test @Test
fun `can be not view the workgroup if is null`(): Unit = listOf(WorkgroupVoter()).run { fun `can not be create workgroup if not connected`() {
mockk<ApplicationCall> { WorkgroupVoter()
every { user } returns tesla.user .canCreate(workgroupPublic, null)
}.let { .vote `should be` DENIED
assertThrows<NoSubjectDefinedException> {
can(WorkgroupVoter.Action.VIEW, it, null)
}
}
} }
@Test @Test
fun `can be view your workgroup list`(): Unit = listOf(WorkgroupVoter()).run { fun `can be delete workgroup if owner`() {
mockk<ApplicationCall> { WorkgroupVoter()
every { user } returns tesla.user .canDelete(workgroupPublic, tesla)
}.let { .vote `should be` GRANTED
listOf(workgroupPublic).map { workgroup ->
can(WorkgroupVoter.Action.VIEW, it, workgroup)
}.all { it } `should be` true
}
} }
@Test @Test
fun `can be create workgroup`(): Unit = listOf(WorkgroupVoter()).run { fun `can not be delete workgroup if not owner`() {
mockk<ApplicationCall> { WorkgroupVoter()
every { user } returns tesla.user .canDelete(workgroupPublic, einstein)
}.let { .vote `should be` DENIED
can(WorkgroupVoter.Action.CREATE, it, workgroupPublic) `should be` true
}
} }
@Test @Test
fun `can not be create workgroup if not connected`(): Unit = listOf(WorkgroupVoter()).run { fun `can be update workgroup if owner`() {
mockk<ApplicationCall> { WorkgroupVoter()
every { user } returns null .canUpdate(workgroupPublic, tesla)
}.let { .vote `should be` GRANTED
can(WorkgroupVoter.Action.CREATE, it, workgroupPublic) `should be` false
}
} }
@Test @Test
fun `can be delete workgroup if owner`(): Unit = listOf(WorkgroupVoter()).run { fun `can not be update workgroup if not owner`() {
mockk<ApplicationCall> { WorkgroupVoter()
every { user } returns tesla.user .canUpdate(workgroupPublic, einstein)
}.let { .vote `should be` DENIED
can(WorkgroupVoter.Action.DELETE, it, workgroupPublic) `should be` true
}
}
@Test
fun `can not be delete workgroup if not owner`(): Unit = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(WorkgroupVoter.Action.DELETE, it, workgroupPublic) `should be` false
}
}
@Test
fun `can be update workgroup if owner`(): Unit = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(WorkgroupVoter.Action.UPDATE, it, workgroupPublic) `should be` true
}
}
@Test
fun `can not be update workgroup if not owner`(): Unit = listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns einstein.user
}.let {
can(WorkgroupVoter.Action.UPDATE, it, workgroupPublic) `should be` false
}
}
@Test
fun `can not be update workgroup if workgroup has no user`() {
Assertions.assertThrows(VoterException::class.java) {
listOf(WorkgroupVoter()).run {
mockk<ApplicationCall> {
every { user } returns tesla.user
}.let {
can(WorkgroupVoter.Action.UPDATE, it, workgroupref)
}
}
}
} }
} }