diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 9377824..592fdc7 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -4,8 +4,8 @@ diff --git a/build.gradle.kts b/build.gradle.kts index dbadaaf..ed29c9a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -164,6 +164,14 @@ tasks.withType { } } +tasks.withType { + kotlinOptions { + jvmTarget = "11" + sourceCompatibility = "11" + targetCompatibility = "11" + } +} + tasks.named("shadowJar") { mergeServiceFiles("META-INF/services") archiveFileName.set("${archiveBaseName.get()}-latest-all.${archiveExtension.get()}") diff --git a/src/main/kotlin/fr/dcproject/component/auth/User.kt b/src/main/kotlin/fr/dcproject/component/auth/User.kt index f2db49f..96fa66e 100644 --- a/src/main/kotlin/fr/dcproject/component/auth/User.kt +++ b/src/main/kotlin/fr/dcproject/component/auth/User.kt @@ -27,6 +27,15 @@ open class User( CreatedAt by CreatedAt.Imp(), UpdatedAt by UpdatedAt.Imp() +class UserCreator( + id: UUID = UUID.randomUUID(), + override val username: String, +) : UserRef(id), UserWithUsername + +interface UserWithUsername { + val username: String +} + interface UserWithPasswordI { val id: UUID val password: String diff --git a/src/main/kotlin/fr/dcproject/component/citizen/Citizen.kt b/src/main/kotlin/fr/dcproject/component/citizen/Citizen.kt index 254690c..f2e71ac 100644 --- a/src/main/kotlin/fr/dcproject/component/citizen/Citizen.kt +++ b/src/main/kotlin/fr/dcproject/component/citizen/Citizen.kt @@ -5,6 +5,7 @@ import fr.dcproject.common.entity.DeletedAt import fr.dcproject.common.entity.Entity import fr.dcproject.common.entity.EntityI import fr.dcproject.component.auth.User +import fr.dcproject.component.auth.UserCreator import fr.dcproject.component.auth.UserForCreate import fr.dcproject.component.auth.UserI import fr.dcproject.component.auth.UserRef @@ -50,6 +51,28 @@ class Citizen( ) } +data class CitizenCreator( + override var id: UUID = UUID.randomUUID(), + override var name: Name, + override var email: String, + override var voteAnonymous: Boolean = true, + override var followAnonymous: Boolean = true, + override val user: UserCreator, + override val deletedAt: DateTime? = null +) : CitizenCreatorI, + CitizenRefWithUser(id, user), + DeletedAt by DeletedAt.Imp(deletedAt) + +interface CitizenCreatorI : CitizenWithUserI, CitizenWithEmail, CitizenCartI, DeletedAt { + override val id: UUID + override val name: Name + override val email: String + val voteAnonymous: Boolean + val followAnonymous: Boolean + override val user: UserCreator + override val deletedAt: DateTime? +} + @Deprecated("") data class CitizenBasic( override var id: UUID = UUID.randomUUID(), diff --git a/src/main/kotlin/fr/dcproject/component/follow/Follow.kt b/src/main/kotlin/fr/dcproject/component/follow/Follow.kt index 3bc05e2..2e398d6 100644 --- a/src/main/kotlin/fr/dcproject/component/follow/Follow.kt +++ b/src/main/kotlin/fr/dcproject/component/follow/Follow.kt @@ -6,27 +6,18 @@ import fr.dcproject.common.entity.EntityI import fr.dcproject.common.entity.ExtraI import fr.dcproject.common.entity.HasTarget import fr.dcproject.common.entity.TargetI -import fr.dcproject.component.citizen.CitizenBasic -import fr.dcproject.component.citizen.CitizenBasicI +import fr.dcproject.component.citizen.CitizenCreator import fr.dcproject.component.citizen.CitizenI +import fr.dcproject.component.citizen.CitizenRef import java.util.UUID -@Deprecated("") -class Follow( +open class FollowForView( id: UUID = UUID.randomUUID(), - override val createdBy: CitizenBasic, + override val createdBy: CitizenCreator, override var target: T -) : ExtraI, - FollowSimple(id, createdBy, target) - -@Deprecated("") -open class FollowSimple( - id: UUID = UUID.randomUUID(), - override val createdBy: C, - override var target: T -) : ExtraI, +) : ExtraI, FollowRef(id), - Created by Created.Imp(createdBy) + Created by Created.Imp(createdBy) class FollowForUpdate( id: UUID = UUID.randomUUID(), diff --git a/src/main/kotlin/fr/dcproject/component/follow/FollowAccessControl.kt b/src/main/kotlin/fr/dcproject/component/follow/FollowAccessControl.kt index 58998c9..9e624cf 100644 --- a/src/main/kotlin/fr/dcproject/component/follow/FollowAccessControl.kt +++ b/src/main/kotlin/fr/dcproject/component/follow/FollowAccessControl.kt @@ -3,7 +3,6 @@ package fr.dcproject.component.follow import fr.dcproject.common.security.AccessControl import fr.dcproject.common.security.AccessResponse import fr.dcproject.component.citizen.CitizenI -import fr.dcproject.component.follow.Follow as FollowEntity class FollowAccessControl : AccessControl() { fun canCreate(subject: FollowI, citizen: CitizenI?): AccessResponse { @@ -16,10 +15,10 @@ class FollowAccessControl : AccessControl() { else granted() } - fun > canView(subjects: List, citizen: CitizenI?): AccessResponse = + fun > canView(subjects: List, citizen: CitizenI?): AccessResponse = canAll(subjects) { canView(it, citizen) } - fun canView(subject: FollowEntity<*>, citizen: CitizenI?): AccessResponse { + fun canView(subject: FollowForView<*>, citizen: CitizenI?): AccessResponse { return if ((citizen != null && subject.createdBy.id == citizen.id) || !subject.createdBy.followAnonymous) granted() else denied("You cannot view an anonymous follow", "follow.view.anonymous") } diff --git a/src/main/kotlin/fr/dcproject/component/follow/FollowRepository.kt b/src/main/kotlin/fr/dcproject/component/follow/FollowRepository.kt index f57c6bc..eff0017 100644 --- a/src/main/kotlin/fr/dcproject/component/follow/FollowRepository.kt +++ b/src/main/kotlin/fr/dcproject/component/follow/FollowRepository.kt @@ -5,7 +5,6 @@ import fr.dcproject.common.entity.TargetRef 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.component.constitution.ConstitutionRef import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Requester @@ -14,21 +13,20 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import java.util.UUID import fr.dcproject.component.constitution.Constitution as ConstitutionEntity -import fr.dcproject.component.follow.Follow as FollowEntity sealed class FollowRepository(override var requester: Requester) : RepositoryI { open fun findByCitizen( citizen: CitizenI, page: Int = 1, limit: Int = 50 - ): Paginated> = + ): Paginated> = findByCitizen(citizen.id, page, limit) open fun findByCitizen( citizenId: UUID, page: Int = 1, limit: Int = 50 - ): Paginated> { + ): Paginated> { return requester .getFunction("find_follows_by_citizen") .select( @@ -61,7 +59,7 @@ sealed class FollowRepository(override var requ open fun findFollow( citizen: CitizenI, target: TargetRef - ): FollowEntity? = + ): FollowForView? = requester .getFunction("find_follow") .selectOne( @@ -73,7 +71,7 @@ sealed class FollowRepository(override var requ fun findFollowsByTarget( target: Entity, bulkSize: Int = 300 - ): Flow> = flow { + ): Flow> = flow { var nextPage = 1 do { val paginate = findFollowsByTarget(target, nextPage, bulkSize) @@ -88,7 +86,7 @@ sealed class FollowRepository(override var requ target: Entity, page: Int = 1, limit: Int = 300 - ): Paginated> + ): Paginated> } class FollowArticleRepository(requester: Requester) : FollowRepository(requester) { @@ -96,7 +94,7 @@ class FollowArticleRepository(requester: Requester) : FollowRepository> { + ): Paginated> { return requester.run { getFunction("find_follows_article_by_citizen") .select( @@ -111,7 +109,7 @@ class FollowArticleRepository(requester: Requester) : FollowRepository> { + ): Paginated> { return requester .getFunction("find_follows_article_by_target") .select( @@ -127,7 +125,7 @@ class FollowConstitutionRepository(requester: Requester) : FollowRepository> { + ): Paginated> { return requester.run { getFunction("find_follows_constitution_by_citizen") .select( @@ -142,7 +140,7 @@ class FollowConstitutionRepository(requester: Requester) : FollowRepository> { + ): Paginated> { TODO("Not yet implemented") } } diff --git a/src/main/kotlin/fr/dcproject/component/notification/NotificationConsumer.kt b/src/main/kotlin/fr/dcproject/component/notification/NotificationConsumer.kt index a5b1a47..e96ffed 100644 --- a/src/main/kotlin/fr/dcproject/component/notification/NotificationConsumer.kt +++ b/src/main/kotlin/fr/dcproject/component/notification/NotificationConsumer.kt @@ -7,10 +7,9 @@ import com.rabbitmq.client.Consumer import com.rabbitmq.client.DefaultConsumer import com.rabbitmq.client.Envelope import fr.dcproject.common.entity.TargetRef -import fr.dcproject.component.citizen.CitizenRef import fr.dcproject.component.follow.FollowArticleRepository import fr.dcproject.component.follow.FollowConstitutionRepository -import fr.dcproject.component.follow.FollowSimple +import fr.dcproject.component.follow.FollowForView import io.ktor.utils.io.errors.IOException import io.lettuce.core.RedisClient import io.lettuce.core.api.async.RedisAsyncCommands @@ -108,6 +107,6 @@ class NotificationConsumer( private class DecodedMessage( val event: EntityNotification, val rawMessage: String, - val follow: FollowSimple + val follow: FollowForView ) } diff --git a/src/main/kotlin/fr/dcproject/component/notification/NotificationEmailSender.kt b/src/main/kotlin/fr/dcproject/component/notification/NotificationEmailSender.kt index 76dd606..15911f3 100644 --- a/src/main/kotlin/fr/dcproject/component/notification/NotificationEmailSender.kt +++ b/src/main/kotlin/fr/dcproject/component/notification/NotificationEmailSender.kt @@ -9,9 +9,8 @@ import fr.dcproject.common.entity.TargetRef import fr.dcproject.component.article.ArticleRepository import fr.dcproject.component.article.ArticleWithTitleI import fr.dcproject.component.citizen.CitizenBasicI -import fr.dcproject.component.citizen.CitizenRef import fr.dcproject.component.citizen.CitizenRepository -import fr.dcproject.component.follow.FollowSimple +import fr.dcproject.component.follow.FollowForView import java.util.UUID class NotificationEmailSender( @@ -20,7 +19,7 @@ class NotificationEmailSender( private val citizenRepo: CitizenRepository, private val articleRepo: ArticleRepository ) { - fun sendEmail(follow: FollowSimple) { + fun sendEmail(follow: FollowForView) { val citizen = citizenRepo.findById(follow.createdBy.id) ?: noCitizen(follow.createdBy.id) val target = when (follow.target.reference) { "article" -> diff --git a/src/test/kotlin/functional/NotificationConsumerTest.kt b/src/test/kotlin/functional/NotificationConsumerTest.kt index 7a675b5..a56af1e 100644 --- a/src/test/kotlin/functional/NotificationConsumerTest.kt +++ b/src/test/kotlin/functional/NotificationConsumerTest.kt @@ -5,9 +5,12 @@ import com.rabbitmq.client.ShutdownSignalException import fr.dcproject.application.Configuration import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleRef +import fr.dcproject.component.auth.UserCreator +import fr.dcproject.component.citizen.CitizenCreator +import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenRef import fr.dcproject.component.follow.FollowArticleRepository -import fr.dcproject.component.follow.FollowSimple +import fr.dcproject.component.follow.FollowForView import fr.dcproject.component.notification.ArticleUpdateNotification import fr.dcproject.component.notification.NotificationConsumer import fr.dcproject.component.notification.NotificationEmailSender @@ -78,8 +81,8 @@ class NotificationConsumerTest { val rabbitFactory: ConnectionFactory = ConnectionFactory().apply { setUri(config.rabbitmq) } val followArticleRepo = mockk { every { findFollowsByTarget(any()) } returns flow { - FollowSimple( - createdBy = CitizenRef(), + FollowForView( + createdBy = CitizenCreator(name = CitizenI.Name("John", "Doe"), email = "john.doe@dc-project.com", user = UserCreator(username = "john-doe")), target = ArticleRef(), ).let { emit(it) } } diff --git a/src/test/kotlin/unit/security/Follow Access Control.kt b/src/test/kotlin/unit/security/Follow Access Control.kt index 1366f0b..8d0aa36 100644 --- a/src/test/kotlin/unit/security/Follow Access Control.kt +++ b/src/test/kotlin/unit/security/Follow Access Control.kt @@ -4,13 +4,14 @@ import fr.dcproject.common.security.AccessDecision.DENIED import fr.dcproject.common.security.AccessDecision.GRANTED import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.auth.User +import fr.dcproject.component.auth.UserCreator import fr.dcproject.component.auth.UserI import fr.dcproject.component.citizen.Citizen -import fr.dcproject.component.citizen.CitizenBasic import fr.dcproject.component.citizen.CitizenCart +import fr.dcproject.component.citizen.CitizenCreator import fr.dcproject.component.citizen.CitizenI -import fr.dcproject.component.follow.Follow import fr.dcproject.component.follow.FollowAccessControl +import fr.dcproject.component.follow.FollowForView import org.amshove.kluent.`should be` import org.joda.time.DateTime import org.junit.jupiter.api.Tag @@ -25,12 +26,10 @@ import java.util.UUID @Execution(CONCURRENT) @Tags(Tag("security"), Tag("unit")) internal class `Follow Access Control` { - private val tesla = CitizenBasic( - user = User( + private val tesla = CitizenCreator( + user = UserCreator( username = "nicolas-tesla", - roles = listOf(UserI.Roles.ROLE_USER) ), - birthday = DateTime.now(), email = "tesla@best.com", name = CitizenI.Name("Nicolas", "Tesla"), followAnonymous = false @@ -46,13 +45,11 @@ internal class `Follow Access Control` { followAnonymous = false ) - private val einstein = CitizenBasic( + private val einstein = CitizenCreator( id = UUID.fromString("319f1226-8f47-4df3-babd-2c7671ad0fbc"), - user = User( + user = UserCreator( username = "albert-einstein", - roles = listOf(UserI.Roles.ROLE_USER) ), - birthday = DateTime.now(), email = "einstein@best.com", name = CitizenI.Name("Albert", "Einstein"), followAnonymous = true @@ -86,12 +83,12 @@ internal class `Follow Access Control` { title = "Super article" ) - private val follow1 = Follow( + private val follow1 = FollowForView( createdBy = tesla, target = article1 ) - private val followAnon = Follow( + private val followAnon = FollowForView( createdBy = einstein, target = article1 )