diff --git a/src/main/kotlin/dev/usbharu/hideout/controller/NoteApController.kt b/src/main/kotlin/dev/usbharu/hideout/controller/NoteApController.kt new file mode 100644 index 00000000..f7fe6197 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/controller/NoteApController.kt @@ -0,0 +1,16 @@ +package dev.usbharu.hideout.controller + +import dev.usbharu.hideout.domain.model.ap.Note +import org.springframework.http.ResponseEntity +import org.springframework.security.core.annotation.CurrentSecurityContext +import org.springframework.security.core.context.SecurityContext +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable + +interface NoteApController { + @GetMapping("/users/*/posts/{postId}") + suspend fun postsAp( + @PathVariable("postId") postId: Long, + @CurrentSecurityContext context: SecurityContext + ): ResponseEntity +} diff --git a/src/main/kotlin/dev/usbharu/hideout/controller/NoteApControllerImpl.kt b/src/main/kotlin/dev/usbharu/hideout/controller/NoteApControllerImpl.kt new file mode 100644 index 00000000..76bd6d63 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/controller/NoteApControllerImpl.kt @@ -0,0 +1,32 @@ +package dev.usbharu.hideout.controller + +import dev.usbharu.hideout.domain.model.ap.Note +import dev.usbharu.hideout.service.api.NoteApApiService +import dev.usbharu.hideout.service.signature.HttpSignatureUser +import org.springframework.http.ResponseEntity +import org.springframework.security.core.annotation.CurrentSecurityContext +import org.springframework.security.core.context.SecurityContext +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RestController + +@RestController +class NoteApControllerImpl(private val noteApApiService: NoteApApiService) : NoteApController { + override suspend fun postsAp( + @PathVariable(value = "postId") postId: Long, @CurrentSecurityContext context: SecurityContext + ): ResponseEntity { + + val userId = + if (context.authentication is PreAuthenticatedAuthenticationToken && context.authentication.details is HttpSignatureUser) { + (context.authentication.details as HttpSignatureUser).id + } else { + null + } + + val note = noteApApiService.getNote(postId, userId) + if (note != null) { + return ResponseEntity.ok(note) + } + return ResponseEntity.notFound().build() + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/query/FollowerQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/query/FollowerQueryService.kt index 4cc73ef1..4922b268 100644 --- a/src/main/kotlin/dev/usbharu/hideout/query/FollowerQueryService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/query/FollowerQueryService.kt @@ -1,9 +1,7 @@ package dev.usbharu.hideout.query import dev.usbharu.hideout.domain.model.hideout.entity.User -import org.springframework.stereotype.Repository -@Repository interface FollowerQueryService { suspend fun findFollowersById(id: Long): List suspend fun findFollowersByNameAndDomain(name: String, domain: String): List diff --git a/src/main/kotlin/dev/usbharu/hideout/query/activitypub/NoteQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/query/activitypub/NoteQueryService.kt new file mode 100644 index 00000000..d0ee3adb --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/query/activitypub/NoteQueryService.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.query.activitypub + +import dev.usbharu.hideout.domain.model.ap.Note +import dev.usbharu.hideout.domain.model.hideout.entity.Post + +interface NoteQueryService { + suspend fun findById(id: Long): Pair +} diff --git a/src/main/kotlin/dev/usbharu/hideout/query/activitypub/NoteQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/query/activitypub/NoteQueryServiceImpl.kt new file mode 100644 index 00000000..1ee6d038 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/query/activitypub/NoteQueryServiceImpl.kt @@ -0,0 +1,39 @@ +package dev.usbharu.hideout.query.activitypub + +import dev.usbharu.hideout.domain.model.ap.Note +import dev.usbharu.hideout.domain.model.hideout.entity.Post +import dev.usbharu.hideout.exception.FailedToGetResourcesException +import dev.usbharu.hideout.repository.Posts +import dev.usbharu.hideout.repository.Users +import dev.usbharu.hideout.repository.toPost +import dev.usbharu.hideout.util.singleOr +import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.sql.select +import org.springframework.stereotype.Repository +import java.time.Instant + +@Repository +class NoteQueryServiceImpl : NoteQueryService { + override suspend fun findById(id: Long): Pair { + return Posts + .leftJoin(Users) + .select { Posts.id eq id } + .singleOr { FailedToGetResourcesException("id $id is duplicate or does not exist.") } + .let { it.toNote() to it.toPost() } + + } + + private fun ResultRow.toNote(): Note { + return Note( + name = "Post", + id = this[Posts.apId], + attributedTo = this[Users.url], + content = this[Posts.text], + published = Instant.ofEpochMilli(this[Posts.createdAt]).toString(), + to = listOf(), + cc = listOf(), + inReplyTo = null, + sensitive = false + ) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/api/NoteApApiService.kt b/src/main/kotlin/dev/usbharu/hideout/service/api/NoteApApiService.kt new file mode 100644 index 00000000..38006f4b --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/service/api/NoteApApiService.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.service.api + +import dev.usbharu.hideout.domain.model.ap.Note + +interface NoteApApiService { + suspend fun getNote(postId: Long, userId: Long?): Note? +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/api/NoteApApiServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/api/NoteApApiServiceImpl.kt new file mode 100644 index 00000000..4fb14cfd --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/service/api/NoteApApiServiceImpl.kt @@ -0,0 +1,37 @@ +package dev.usbharu.hideout.service.api + +import dev.usbharu.hideout.domain.model.ap.Note +import dev.usbharu.hideout.domain.model.hideout.entity.Visibility +import dev.usbharu.hideout.query.FollowerQueryService +import dev.usbharu.hideout.query.activitypub.NoteQueryService +import dev.usbharu.hideout.service.core.Transaction +import org.springframework.stereotype.Service + +@Service +class NoteApApiServiceImpl( + private val noteQueryService: NoteQueryService, + private val followerQueryService: FollowerQueryService, + private val transaction: Transaction +) : NoteApApiService { + override suspend fun getNote(postId: Long, userId: Long?): Note? = transaction.transaction { + val findById = noteQueryService.findById(postId) + when (findById.second.visibility) { + Visibility.PUBLIC, Visibility.UNLISTED -> { + return@transaction findById.first + } + + Visibility.FOLLOWERS -> { + if (userId == null) { + return@transaction null + } + + if (followerQueryService.alreadyFollow(findById.second.userId, userId).not()) { + return@transaction null + } + return@transaction findById.first + } + + Visibility.DIRECT -> return@transaction null + } + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/signature/HttpSignatureUser.kt b/src/main/kotlin/dev/usbharu/hideout/service/signature/HttpSignatureUser.kt index d9c55816..ad1b1859 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/signature/HttpSignatureUser.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/signature/HttpSignatureUser.kt @@ -7,6 +7,7 @@ import java.io.Serial class HttpSignatureUser( username: String, val domain: String, + val id: Long, credentialsNonExpired: Boolean, accountNonLocked: Boolean, authorities: MutableCollection? @@ -19,6 +20,26 @@ class HttpSignatureUser( accountNonLocked, authorities ) { + + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is HttpSignatureUser) return false + if (!super.equals(other)) return false + + if (domain != other.domain) return false + if (id != other.id) return false + + return true + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + domain.hashCode() + result = 31 * result + id.hashCode() + return result + } + companion object { @Serial private const val serialVersionUID: Long = -3330552099960982997L diff --git a/src/main/kotlin/dev/usbharu/hideout/service/signature/HttpSignatureUserDetailsService.kt b/src/main/kotlin/dev/usbharu/hideout/service/signature/HttpSignatureUserDetailsService.kt index f86d7bfb..d1d619ca 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/signature/HttpSignatureUserDetailsService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/signature/HttpSignatureUserDetailsService.kt @@ -49,6 +49,7 @@ class HttpSignatureUserDetailsService( HttpSignatureUser( username = findByKeyId.name, domain = findByKeyId.domain, + id = findByKeyId.id, credentialsNonExpired = true, accountNonLocked = true, authorities = mutableListOf()