mirror of https://github.com/usbharu/Hideout.git
Merge pull request #10 from usbharu/feature/refactor-user
Feature/refactor user
This commit is contained in:
commit
1a6edbdac6
|
@ -8,12 +8,17 @@ import dev.usbharu.hideout.config.ConfigData
|
||||||
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
||||||
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
||||||
import dev.usbharu.hideout.plugins.*
|
import dev.usbharu.hideout.plugins.*
|
||||||
import dev.usbharu.hideout.repository.*
|
import dev.usbharu.hideout.repository.IPostRepository
|
||||||
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
|
import dev.usbharu.hideout.repository.PostRepositoryImpl
|
||||||
|
import dev.usbharu.hideout.repository.UserRepository
|
||||||
import dev.usbharu.hideout.routing.register
|
import dev.usbharu.hideout.routing.register
|
||||||
import dev.usbharu.hideout.service.IPostService
|
import dev.usbharu.hideout.service.IPostService
|
||||||
import dev.usbharu.hideout.service.IUserAuthService
|
import dev.usbharu.hideout.service.IUserAuthService
|
||||||
|
import dev.usbharu.hideout.service.IdGenerateService
|
||||||
import dev.usbharu.hideout.service.TwitterSnowflakeIdGenerateService
|
import dev.usbharu.hideout.service.TwitterSnowflakeIdGenerateService
|
||||||
import dev.usbharu.hideout.service.activitypub.*
|
import dev.usbharu.hideout.service.activitypub.*
|
||||||
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import dev.usbharu.hideout.service.impl.PostService
|
import dev.usbharu.hideout.service.impl.PostService
|
||||||
import dev.usbharu.hideout.service.impl.UserAuthService
|
import dev.usbharu.hideout.service.impl.UserAuthService
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.UserService
|
||||||
|
@ -57,9 +62,8 @@ fun Application.parent() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
single<IUserRepository> { UserRepository(get()) }
|
single<IUserRepository> { UserRepository(get(),get()) }
|
||||||
single<IUserAuthRepository> { UserAuthRepository(get()) }
|
single<IUserAuthService> { UserAuthService(get()) }
|
||||||
single<IUserAuthService> { UserAuthService(get(), get()) }
|
|
||||||
single<HttpSignatureVerifyService> { HttpSignatureVerifyServiceImpl(get()) }
|
single<HttpSignatureVerifyService> { HttpSignatureVerifyServiceImpl(get()) }
|
||||||
single<JobQueueParentService> {
|
single<JobQueueParentService> {
|
||||||
val kJobJobQueueService = KJobJobQueueParentService(get())
|
val kJobJobQueueService = KJobJobQueueParentService(get())
|
||||||
|
@ -79,11 +83,12 @@ fun Application.parent() {
|
||||||
}
|
}
|
||||||
single<ActivityPubFollowService> { ActivityPubFollowServiceImpl(get(), get(), get(), get()) }
|
single<ActivityPubFollowService> { ActivityPubFollowServiceImpl(get(), get(), get(), get()) }
|
||||||
single<ActivityPubService> { ActivityPubServiceImpl(get(), get()) }
|
single<ActivityPubService> { ActivityPubServiceImpl(get(), get()) }
|
||||||
single<UserService> { UserService(get()) }
|
single<IUserService> { UserService(get(),get()) }
|
||||||
single<ActivityPubUserService> { ActivityPubUserServiceImpl(get(), get(), get()) }
|
single<ActivityPubUserService> { ActivityPubUserServiceImpl(get(), get()) }
|
||||||
single<ActivityPubNoteService> { ActivityPubNoteServiceImpl(get(), get(), get()) }
|
single<ActivityPubNoteService> { ActivityPubNoteServiceImpl(get(), get(), get()) }
|
||||||
single<IPostService> { PostService(get(), get()) }
|
single<IPostService> { PostService(get(), get()) }
|
||||||
single<IPostRepository> { PostRepositoryImpl(get(), TwitterSnowflakeIdGenerateService) }
|
single<IPostRepository> { PostRepositoryImpl(get(), get()) }
|
||||||
|
single<IdGenerateService> {TwitterSnowflakeIdGenerateService}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,11 +97,11 @@ fun Application.parent() {
|
||||||
configureStaticRouting()
|
configureStaticRouting()
|
||||||
configureMonitoring()
|
configureMonitoring()
|
||||||
configureSerialization()
|
configureSerialization()
|
||||||
register(inject<IUserAuthService>().value)
|
register(inject<IUserService>().value)
|
||||||
configureRouting(
|
configureRouting(
|
||||||
inject<HttpSignatureVerifyService>().value,
|
inject<HttpSignatureVerifyService>().value,
|
||||||
inject<ActivityPubService>().value,
|
inject<ActivityPubService>().value,
|
||||||
inject<UserService>().value,
|
inject<IUserService>().value,
|
||||||
inject<ActivityPubUserService>().value,
|
inject<ActivityPubUserService>().value,
|
||||||
inject<IPostService>().value
|
inject<IPostService>().value
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.usbharu.hideout.domain.model
|
package dev.usbharu.hideout.domain.model
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.repository.Users
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
import org.jetbrains.exposed.sql.ResultRow
|
||||||
import org.jetbrains.exposed.sql.Table
|
import org.jetbrains.exposed.sql.Table
|
||||||
|
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
package dev.usbharu.hideout.domain.model
|
|
||||||
|
|
||||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
|
||||||
|
|
||||||
data class User(
|
|
||||||
val name: String,
|
|
||||||
val domain: String,
|
|
||||||
val screenName: String,
|
|
||||||
val description: String,
|
|
||||||
val inbox: String,
|
|
||||||
val outbox: String,
|
|
||||||
val url: String
|
|
||||||
)
|
|
||||||
|
|
||||||
data class UserEntity(
|
|
||||||
val id: Long,
|
|
||||||
val name: String,
|
|
||||||
val domain: String,
|
|
||||||
val screenName: String,
|
|
||||||
val description: String,
|
|
||||||
val inbox: String,
|
|
||||||
val outbox: String,
|
|
||||||
val url: String
|
|
||||||
) {
|
|
||||||
constructor(id: Long, user: User) : this(
|
|
||||||
id,
|
|
||||||
user.name,
|
|
||||||
user.domain,
|
|
||||||
user.screenName,
|
|
||||||
user.description,
|
|
||||||
user.inbox,
|
|
||||||
user.outbox,
|
|
||||||
user.url
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
object Users : LongIdTable("users") {
|
|
||||||
val name = varchar("name", length = 64)
|
|
||||||
val domain = varchar("domain", length = 255)
|
|
||||||
val screenName = varchar("screen_name", length = 64)
|
|
||||||
val description = varchar("description", length = 600)
|
|
||||||
val inbox = varchar("inbox", length = 255).uniqueIndex()
|
|
||||||
val outbox = varchar("outbox", length = 255).uniqueIndex()
|
|
||||||
val url = varchar("url", length = 255).uniqueIndex()
|
|
||||||
|
|
||||||
init {
|
|
||||||
uniqueIndex(name, domain)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun ResultRow.toUser(): User {
|
|
||||||
return User(
|
|
||||||
this[Users.name],
|
|
||||||
this[Users.domain],
|
|
||||||
this[Users.screenName],
|
|
||||||
this[Users.description],
|
|
||||||
this[Users.inbox],
|
|
||||||
this[Users.outbox],
|
|
||||||
this[Users.url]
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package dev.usbharu.hideout.domain.model
|
|
||||||
|
|
||||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
|
||||||
import org.jetbrains.exposed.sql.ReferenceOption
|
|
||||||
|
|
||||||
data class UserAuthentication(
|
|
||||||
val userId: Long,
|
|
||||||
val hash: String?,
|
|
||||||
val publicKey: String,
|
|
||||||
val privateKey: String?
|
|
||||||
)
|
|
||||||
|
|
||||||
data class UserAuthenticationEntity(
|
|
||||||
val id: Long,
|
|
||||||
val userId: Long,
|
|
||||||
val hash: String?,
|
|
||||||
val publicKey: String,
|
|
||||||
val privateKey: String?
|
|
||||||
) {
|
|
||||||
constructor(id: Long, userAuthentication: UserAuthentication) : this(
|
|
||||||
id,
|
|
||||||
userAuthentication.userId,
|
|
||||||
userAuthentication.hash,
|
|
||||||
userAuthentication.publicKey,
|
|
||||||
userAuthentication.privateKey
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
object UsersAuthentication : LongIdTable("users_auth") {
|
|
||||||
val userId = long("user_id").references(Users.id, onUpdate = ReferenceOption.CASCADE)
|
|
||||||
val hash = varchar("hash", length = 64).nullable()
|
|
||||||
val publicKey = varchar("public_key", length = 1000_000)
|
|
||||||
val privateKey = varchar("private_key", length = 1000_000).nullable()
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package dev.usbharu.hideout.domain.model
|
|
||||||
|
|
||||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
|
||||||
|
|
||||||
object UsersFollowers : LongIdTable("users_followers") {
|
|
||||||
val userId = long("user_id").references(Users.id).index()
|
|
||||||
val followerId = long("follower_id").references(Users.id)
|
|
||||||
init {
|
|
||||||
uniqueIndex(userId, followerId)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +1,16 @@
|
||||||
package dev.usbharu.hideout.domain.model.ap
|
package dev.usbharu.hideout.domain.model.ap
|
||||||
|
|
||||||
open class Accept : Object {
|
open class Accept : Object {
|
||||||
public var `object`: Object? = null
|
var `object`: Object? = null
|
||||||
public var actor:String? = null
|
|
||||||
protected constructor() : super()
|
protected constructor() : super()
|
||||||
constructor(
|
constructor(
|
||||||
type: List<String> = emptyList(),
|
type: List<String> = emptyList(),
|
||||||
name: String,
|
name: String,
|
||||||
`object`: Object?,
|
`object`: Object?,
|
||||||
actor: String?
|
actor: String?
|
||||||
) : super(add(type,"Accept"), name) {
|
) : super(add(type, "Accept"), name,actor) {
|
||||||
this.`object` = `object`
|
this.`object` = `object`
|
||||||
this.actor = actor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
|
|
@ -4,7 +4,18 @@ open class Create : Object {
|
||||||
var `object`: Object? = null
|
var `object`: Object? = null
|
||||||
|
|
||||||
protected constructor() : super()
|
protected constructor() : super()
|
||||||
constructor(type: List<String> = emptyList(), name: String, `object`: Object?) : super(add(type,"Create"), name) {
|
constructor(
|
||||||
|
type: List<String> = emptyList(),
|
||||||
|
name: String? = null,
|
||||||
|
`object`: Object?,
|
||||||
|
actor: String? = null,
|
||||||
|
id: String? = null
|
||||||
|
) : super(
|
||||||
|
add(type, "Create"),
|
||||||
|
name,
|
||||||
|
actor,
|
||||||
|
id
|
||||||
|
) {
|
||||||
this.`object` = `object`
|
this.`object` = `object`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
package dev.usbharu.hideout.domain.model.ap
|
package dev.usbharu.hideout.domain.model.ap
|
||||||
|
|
||||||
open class Follow : Object {
|
open class Follow : Object {
|
||||||
public var `object`:String? = null
|
var `object`: String? = null
|
||||||
public var actor:String? = null
|
|
||||||
protected constructor() : super()
|
protected constructor() : super()
|
||||||
constructor(
|
constructor(
|
||||||
type: List<String> = emptyList(),
|
type: List<String> = emptyList(),
|
||||||
name: String,
|
name: String,
|
||||||
`object`: String?,
|
`object`: String?,
|
||||||
actor: String?
|
actor: String?
|
||||||
) : super(add(type,"Follow"), name) {
|
) : super(add(type, "Follow"), name,actor) {
|
||||||
this.`object` = `object`
|
this.`object` = `object`
|
||||||
this.actor = actor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package dev.usbharu.hideout.domain.model.ap
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonAutoDetect
|
import com.fasterxml.jackson.annotation.JsonAutoDetect
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator
|
import com.fasterxml.jackson.annotation.JsonCreator
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.fasterxml.jackson.core.JsonGenerator
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
import com.fasterxml.jackson.databind.JsonDeserializer
|
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||||
|
@ -15,7 +16,8 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||||
open class JsonLd {
|
open class JsonLd {
|
||||||
@JsonProperty("@context")
|
@JsonProperty("@context")
|
||||||
@JsonDeserialize(contentUsing = ContextDeserializer::class)
|
@JsonDeserialize(contentUsing = ContextDeserializer::class)
|
||||||
@JsonSerialize(using = ContextSerializer::class)
|
@JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY, using = ContextSerializer::class)
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
var context: List<String> = emptyList()
|
var context: List<String> = emptyList()
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
|
@ -43,8 +45,11 @@ open class JsonLd {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ContextDeserializer : JsonDeserializer<String>() {
|
class ContextDeserializer : JsonDeserializer<String>() {
|
||||||
override fun deserialize(p0: com.fasterxml.jackson.core.JsonParser?, p1: com.fasterxml.jackson.databind.DeserializationContext?): String {
|
override fun deserialize(
|
||||||
|
p0: com.fasterxml.jackson.core.JsonParser?,
|
||||||
|
p1: com.fasterxml.jackson.databind.DeserializationContext?
|
||||||
|
): String {
|
||||||
val readTree: JsonNode = p0?.codec?.readTree(p0) ?: return ""
|
val readTree: JsonNode = p0?.codec?.readTree(p0) ?: return ""
|
||||||
if (readTree.isObject) {
|
if (readTree.isObject) {
|
||||||
return ""
|
return ""
|
||||||
|
@ -53,17 +58,23 @@ public class ContextDeserializer : JsonDeserializer<String>() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ContextSerializer : JsonSerializer<List<String>>() {
|
class ContextSerializer : JsonSerializer<List<String>>() {
|
||||||
|
|
||||||
|
|
||||||
|
override fun isEmpty(value: List<String>?): Boolean {
|
||||||
|
return value.isNullOrEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
override fun serialize(value: List<String>?, gen: JsonGenerator?, serializers: SerializerProvider?) {
|
override fun serialize(value: List<String>?, gen: JsonGenerator?, serializers: SerializerProvider?) {
|
||||||
if (value.isNullOrEmpty()) {
|
if (value.isNullOrEmpty()) {
|
||||||
gen?.writeNull()
|
gen?.writeNull()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (value?.size == 1) {
|
if (value.size == 1) {
|
||||||
gen?.writeString(value[0])
|
gen?.writeString(value[0])
|
||||||
} else {
|
} else {
|
||||||
gen?.writeStartArray()
|
gen?.writeStartArray()
|
||||||
value?.forEach {
|
value.forEach {
|
||||||
gen?.writeString(it)
|
gen?.writeString(it)
|
||||||
}
|
}
|
||||||
gen?.writeEndArray()
|
gen?.writeEndArray()
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package dev.usbharu.hideout.domain.model.ap
|
package dev.usbharu.hideout.domain.model.ap
|
||||||
|
|
||||||
open class Key : Object {
|
open class Key : Object {
|
||||||
var id:String? = null
|
|
||||||
var owner: String? = null
|
var owner: String? = null
|
||||||
var publicKeyPem: String? = null
|
var publicKeyPem: String? = null
|
||||||
|
|
||||||
protected constructor() : super()
|
protected constructor() : super()
|
||||||
constructor(
|
constructor(
|
||||||
type: List<String>,
|
type: List<String>,
|
||||||
|
@ -11,8 +11,7 @@ open class Key : Object {
|
||||||
id: String?,
|
id: String?,
|
||||||
owner: String?,
|
owner: String?,
|
||||||
publicKeyPem: String?
|
publicKeyPem: String?
|
||||||
) : super(add(type,"Key"), name) {
|
) : super(add(type, "Key"), name,id) {
|
||||||
this.id = id
|
|
||||||
this.owner = owner
|
this.owner = owner
|
||||||
this.publicKeyPem = publicKeyPem
|
this.publicKeyPem = publicKeyPem
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package dev.usbharu.hideout.domain.model.ap
|
package dev.usbharu.hideout.domain.model.ap
|
||||||
|
|
||||||
open class Note : Object {
|
open class Note : Object {
|
||||||
var id:String? = null
|
|
||||||
var attributedTo: String? = null
|
var attributedTo: String? = null
|
||||||
var content: String? = null
|
var content: String? = null
|
||||||
var published: String? = null
|
var published: String? = null
|
||||||
var to: List<String> = emptyList()
|
var to: List<String> = emptyList()
|
||||||
|
|
||||||
protected constructor() : super()
|
protected constructor() : super()
|
||||||
constructor(
|
constructor(
|
||||||
type: List<String> = emptyList(),
|
type: List<String> = emptyList(),
|
||||||
|
@ -15,8 +15,11 @@ open class Note : Object {
|
||||||
content: String?,
|
content: String?,
|
||||||
published: String?,
|
published: String?,
|
||||||
to: List<String> = emptyList()
|
to: List<String> = emptyList()
|
||||||
) : super(add(type,"Note"), name) {
|
) : super(
|
||||||
this.id = id
|
type = add(type, "Note"),
|
||||||
|
name = name,
|
||||||
|
id = id
|
||||||
|
) {
|
||||||
this.attributedTo = attributedTo
|
this.attributedTo = attributedTo
|
||||||
this.content = content
|
this.content = content
|
||||||
this.published = published
|
this.published = published
|
||||||
|
|
|
@ -9,11 +9,15 @@ open class Object : JsonLd {
|
||||||
@JsonSerialize(using = TypeSerializer::class)
|
@JsonSerialize(using = TypeSerializer::class)
|
||||||
private var type: List<String> = emptyList()
|
private var type: List<String> = emptyList()
|
||||||
var name: String? = null
|
var name: String? = null
|
||||||
|
var actor: String? = null
|
||||||
|
var id:String? = null
|
||||||
|
|
||||||
protected constructor()
|
protected constructor()
|
||||||
constructor(type: List<String>, name: String) : super() {
|
constructor(type: List<String>, name: String? = null,actor:String? = null,id:String? = null) : super() {
|
||||||
this.type = type
|
this.type = type
|
||||||
this.name = name
|
this.name = name
|
||||||
|
this.actor = actor
|
||||||
|
this.id = id
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -25,28 +29,34 @@ open class Object : JsonLd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (other !is Object) return false
|
if (other !is Object) return false
|
||||||
|
if (!super.equals(other)) return false
|
||||||
|
|
||||||
if (type != other.type) return false
|
if (type != other.type) return false
|
||||||
return name == other.name
|
if (name != other.name) return false
|
||||||
|
return actor == other.actor
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = type.hashCode()
|
var result = super.hashCode()
|
||||||
|
result = 31 * result + type.hashCode()
|
||||||
result = 31 * result + (name?.hashCode() ?: 0)
|
result = 31 * result + (name?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (actor?.hashCode() ?: 0)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Object(type=$type, name=$name) ${super.toString()}"
|
return "Object(type=$type, name=$name, actor=$actor) ${super.toString()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TypeSerializer : JsonSerializer<List<String>>() {
|
class TypeSerializer : JsonSerializer<List<String>>() {
|
||||||
override fun serialize(value: List<String>?, gen: JsonGenerator?, serializers: SerializerProvider?) {
|
override fun serialize(value: List<String>?, gen: JsonGenerator?, serializers: SerializerProvider?) {
|
||||||
println(value)
|
println(value)
|
||||||
if (value?.size == 1) {
|
if (value?.size == 1) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package dev.usbharu.hideout.domain.model.ap
|
package dev.usbharu.hideout.domain.model.ap
|
||||||
|
|
||||||
open class Person : Object {
|
open class Person : Object {
|
||||||
private var id:String? = null
|
|
||||||
var preferredUsername: String? = null
|
var preferredUsername: String? = null
|
||||||
var summary: String? = null
|
var summary: String? = null
|
||||||
var inbox: String? = null
|
var inbox: String? = null
|
||||||
|
@ -9,6 +8,7 @@ open class Person : Object {
|
||||||
private var url: String? = null
|
private var url: String? = null
|
||||||
private var icon: Image? = null
|
private var icon: Image? = null
|
||||||
var publicKey: Key? = null
|
var publicKey: Key? = null
|
||||||
|
|
||||||
protected constructor() : super()
|
protected constructor() : super()
|
||||||
constructor(
|
constructor(
|
||||||
type: List<String> = emptyList(),
|
type: List<String> = emptyList(),
|
||||||
|
@ -21,8 +21,7 @@ open class Person : Object {
|
||||||
url: String?,
|
url: String?,
|
||||||
icon: Image?,
|
icon: Image?,
|
||||||
publicKey: Key?
|
publicKey: Key?
|
||||||
) : super(add(type,"Person"), name) {
|
) : super(add(type, "Person"), name,id = id) {
|
||||||
this.id = id
|
|
||||||
this.preferredUsername = preferredUsername
|
this.preferredUsername = preferredUsername
|
||||||
this.summary = summary
|
this.summary = summary
|
||||||
this.inbox = inbox
|
this.inbox = inbox
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package dev.usbharu.hideout.domain.model.hideout.dto
|
||||||
|
|
||||||
|
data class RemoteUserCreateDto(
|
||||||
|
val name:String,
|
||||||
|
val domain:String,
|
||||||
|
val screenName:String,
|
||||||
|
val description:String,
|
||||||
|
val inbox:String,
|
||||||
|
val outbox:String,
|
||||||
|
val url:String,
|
||||||
|
val publicKey:String,
|
||||||
|
)
|
|
@ -0,0 +1,8 @@
|
||||||
|
package dev.usbharu.hideout.domain.model.hideout.dto
|
||||||
|
|
||||||
|
data class UserCreateDto(
|
||||||
|
val name:String,
|
||||||
|
val screenName:String,
|
||||||
|
val description:String,
|
||||||
|
val password:String
|
||||||
|
)
|
|
@ -0,0 +1,18 @@
|
||||||
|
package dev.usbharu.hideout.domain.model.hideout.entity
|
||||||
|
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
data class User(
|
||||||
|
val id: Long,
|
||||||
|
val name: String,
|
||||||
|
val domain: String,
|
||||||
|
val screenName: String,
|
||||||
|
val description: String,
|
||||||
|
val password: String? = null,
|
||||||
|
val inbox: String,
|
||||||
|
val outbox: String,
|
||||||
|
val url: String,
|
||||||
|
val publicKey: String,
|
||||||
|
val privateKey: String? = null,
|
||||||
|
val createdAt: Instant
|
||||||
|
)
|
|
@ -1,8 +1,8 @@
|
||||||
package dev.usbharu.hideout.plugins
|
package dev.usbharu.hideout.plugins
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.ap.JsonLd
|
|
||||||
import dev.usbharu.hideout.config.Config
|
import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.service.IUserAuthService
|
import dev.usbharu.hideout.domain.model.ap.JsonLd
|
||||||
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
import dev.usbharu.hideout.service.impl.UserAuthService
|
import dev.usbharu.hideout.service.impl.UserAuthService
|
||||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
|
@ -44,6 +44,13 @@ suspend fun HttpClient.postAp(urlString: String, username: String, jsonLd: JsonL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun HttpClient.getAp(urlString: String, username: String): HttpResponse {
|
||||||
|
return this.get(urlString) {
|
||||||
|
header("Accept", ContentType.Application.Activity)
|
||||||
|
header("Signature", "keyId=\"$username\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class HttpSignaturePluginConfig {
|
class HttpSignaturePluginConfig {
|
||||||
lateinit var keyMap: KeyMap
|
lateinit var keyMap: KeyMap
|
||||||
}
|
}
|
||||||
|
@ -56,6 +63,7 @@ val httpSignaturePlugin = createClientPlugin("HttpSign", ::HttpSignaturePluginCo
|
||||||
|
|
||||||
|
|
||||||
request.header("Date", format.format(Date()))
|
request.header("Date", format.format(Date()))
|
||||||
|
request.header("Host", "${request.url.host}")
|
||||||
println(request.bodyType)
|
println(request.bodyType)
|
||||||
println(request.bodyType?.type)
|
println(request.bodyType?.type)
|
||||||
if (request.bodyType?.type == String::class) {
|
if (request.bodyType?.type == String::class) {
|
||||||
|
@ -104,6 +112,10 @@ val httpSignaturePlugin = createClientPlugin("HttpSign", ::HttpSignaturePluginCo
|
||||||
"Date"
|
"Date"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"host" -> {
|
||||||
|
"Host"
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
|
@ -139,19 +151,24 @@ val httpSignaturePlugin = createClientPlugin("HttpSign", ::HttpSignaturePluginCo
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
val signatureHeader = request.headers.getAll("Signature").orEmpty()
|
||||||
|
request.headers.remove("Signature")
|
||||||
|
signatureHeader.map { it.replace("; ", ",").replace(";", ",") }.joinToString(",")
|
||||||
|
.let { request.header("Signature", it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class KtorKeyMap(private val userAuthRepository: IUserAuthService) : KeyMap {
|
class KtorKeyMap(private val userAuthRepository: IUserRepository) : KeyMap {
|
||||||
override fun getPublicKey(keyId: String?): PublicKey = runBlocking {
|
override fun getPublicKey(keyId: String?): PublicKey = runBlocking {
|
||||||
val username = (keyId ?: throw IllegalArgumentException("keyId is null")).substringBeforeLast("#pubkey")
|
val username = (keyId ?: throw IllegalArgumentException("keyId is null")).substringBeforeLast("#pubkey")
|
||||||
.substringAfterLast("/")
|
.substringAfterLast("/")
|
||||||
val publicBytes = Base64.getDecoder().decode(
|
val publicBytes = Base64.getDecoder().decode(
|
||||||
userAuthRepository.findByUsername(
|
userAuthRepository.findByNameAndDomain(
|
||||||
username
|
username, Config.configData.domain
|
||||||
).publicKey?.replace("-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----")?.replace("", "")
|
)?.publicKey?.replace("-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----")?.replace("", "")
|
||||||
?.replace("\n", "")
|
?.replace("\n", "")
|
||||||
)
|
)
|
||||||
val x509EncodedKeySpec = X509EncodedKeySpec(publicBytes)
|
val x509EncodedKeySpec = X509EncodedKeySpec(publicBytes)
|
||||||
|
@ -162,9 +179,9 @@ class KtorKeyMap(private val userAuthRepository: IUserAuthService) : KeyMap {
|
||||||
val username = (keyId ?: throw IllegalArgumentException("keyId is null")).substringBeforeLast("#pubkey")
|
val username = (keyId ?: throw IllegalArgumentException("keyId is null")).substringBeforeLast("#pubkey")
|
||||||
.substringAfterLast("/")
|
.substringAfterLast("/")
|
||||||
val publicBytes = Base64.getDecoder().decode(
|
val publicBytes = Base64.getDecoder().decode(
|
||||||
userAuthRepository.findByUsername(
|
userAuthRepository.findByNameAndDomain(
|
||||||
username
|
username, Config.configData.domain
|
||||||
).privateKey?.replace("-----BEGIN PRIVATE KEY-----", "")?.replace("-----END PRIVATE KEY-----", "")
|
)?.privateKey?.replace("-----BEGIN PRIVATE KEY-----", "")?.replace("-----END PRIVATE KEY-----", "")
|
||||||
?.replace("\n", "")
|
?.replace("\n", "")
|
||||||
)
|
)
|
||||||
val x509EncodedKeySpec = PKCS8EncodedKeySpec(publicBytes)
|
val x509EncodedKeySpec = PKCS8EncodedKeySpec(publicBytes)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package dev.usbharu.hideout.plugins
|
package dev.usbharu.hideout.plugins
|
||||||
|
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.plugins.cors.routing.*
|
import io.ktor.server.plugins.cors.routing.*
|
||||||
import io.ktor.server.plugins.defaultheaders.*
|
import io.ktor.server.plugins.defaultheaders.*
|
||||||
import io.ktor.server.plugins.forwardedheaders.*
|
import io.ktor.server.plugins.forwardedheaders.*
|
||||||
import io.ktor.server.application.*
|
|
||||||
|
|
||||||
fun Application.configureHTTP() {
|
fun Application.configureHTTP() {
|
||||||
install(CORS) {
|
install(CORS) {
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package dev.usbharu.hideout.plugins
|
package dev.usbharu.hideout.plugins
|
||||||
|
|
||||||
import io.ktor.server.plugins.callloging.*
|
|
||||||
import org.slf4j.event.*
|
|
||||||
import io.ktor.server.request.*
|
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.plugins.callloging.*
|
||||||
|
import org.slf4j.event.Level
|
||||||
|
|
||||||
fun Application.configureMonitoring() {
|
fun Application.configureMonitoring() {
|
||||||
install(CallLogging) {
|
install(CallLogging) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import dev.usbharu.hideout.routing.wellknown.webfinger
|
||||||
import dev.usbharu.hideout.service.IPostService
|
import dev.usbharu.hideout.service.IPostService
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.plugins.autohead.*
|
import io.ktor.server.plugins.autohead.*
|
||||||
|
@ -17,7 +17,7 @@ import io.ktor.server.routing.*
|
||||||
fun Application.configureRouting(
|
fun Application.configureRouting(
|
||||||
httpSignatureVerifyService: HttpSignatureVerifyService,
|
httpSignatureVerifyService: HttpSignatureVerifyService,
|
||||||
activityPubService: ActivityPubService,
|
activityPubService: ActivityPubService,
|
||||||
userService: UserService,
|
userService: IUserService,
|
||||||
activityPubUserService: ActivityPubUserService,
|
activityPubUserService: ActivityPubUserService,
|
||||||
postService: IPostService
|
postService: IPostService
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -3,8 +3,6 @@ package dev.usbharu.hideout.plugins
|
||||||
import dev.usbharu.hideout.service.IUserAuthService
|
import dev.usbharu.hideout.service.IUserAuthService
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.auth.*
|
import io.ktor.server.auth.*
|
||||||
import io.ktor.server.sessions.*
|
|
||||||
import kotlin.collections.set
|
|
||||||
|
|
||||||
data class UserSession(val username: String) : Principal
|
data class UserSession(val username: String) : Principal
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,9 @@ import com.fasterxml.jackson.annotation.JsonInclude
|
||||||
import com.fasterxml.jackson.annotation.JsonSetter
|
import com.fasterxml.jackson.annotation.JsonSetter
|
||||||
import com.fasterxml.jackson.annotation.Nulls
|
import com.fasterxml.jackson.annotation.Nulls
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
|
||||||
import io.ktor.http.*
|
|
||||||
import io.ktor.serialization.jackson.*
|
import io.ktor.serialization.jackson.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.plugins.contentnegotiation.*
|
import io.ktor.server.plugins.contentnegotiation.*
|
||||||
import io.ktor.server.response.*
|
|
||||||
import io.ktor.server.routing.*
|
|
||||||
|
|
||||||
fun Application.configureSerialization() {
|
fun Application.configureSerialization() {
|
||||||
install(ContentNegotiation) {
|
install(ContentNegotiation) {
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
package dev.usbharu.hideout.repository
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthentication
|
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthenticationEntity
|
|
||||||
|
|
||||||
interface IUserAuthRepository {
|
|
||||||
suspend fun create(userAuthentication: UserAuthentication):UserAuthenticationEntity
|
|
||||||
|
|
||||||
suspend fun findById(id:Long):UserAuthenticationEntity?
|
|
||||||
|
|
||||||
suspend fun update(userAuthenticationEntity: UserAuthenticationEntity)
|
|
||||||
|
|
||||||
suspend fun delete(id:Long)
|
|
||||||
suspend fun findByUserId(id: Long): UserAuthenticationEntity?
|
|
||||||
}
|
|
|
@ -1,32 +1,38 @@
|
||||||
package dev.usbharu.hideout.repository
|
package dev.usbharu.hideout.repository
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.User
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
import dev.usbharu.hideout.domain.model.UserEntity
|
|
||||||
|
|
||||||
interface IUserRepository {
|
interface IUserRepository {
|
||||||
suspend fun create(user: User): UserEntity
|
suspend fun save(user: User): User
|
||||||
|
|
||||||
suspend fun findById(id: Long): UserEntity?
|
suspend fun findById(id: Long): User?
|
||||||
|
|
||||||
suspend fun findByIds(ids: List<Long>): List<UserEntity>
|
suspend fun findByIds(ids: List<Long>): List<User>
|
||||||
|
|
||||||
suspend fun findByName(name: String): UserEntity?
|
suspend fun findByName(name: String): List<User>
|
||||||
|
|
||||||
suspend fun findByNameAndDomains(names: List<Pair<String,String>>): List<UserEntity>
|
suspend fun findByNameAndDomain(name: String, domain: String): User?
|
||||||
|
|
||||||
suspend fun findByUrl(url:String):UserEntity?
|
suspend fun findByDomain(domain:String): List<User>
|
||||||
|
|
||||||
suspend fun findByUrls(urls: List<String>): List<UserEntity>
|
suspend fun findByNameAndDomains(names: List<Pair<String,String>>): List<User>
|
||||||
|
|
||||||
suspend fun update(userEntity: UserEntity)
|
suspend fun findByUrl(url:String): User?
|
||||||
|
|
||||||
|
suspend fun findByUrls(urls: List<String>): List<User>
|
||||||
|
|
||||||
|
@Deprecated("", ReplaceWith("save(userEntity)"))
|
||||||
|
suspend fun update(userEntity: User) = save(userEntity)
|
||||||
|
|
||||||
suspend fun delete(id: Long)
|
suspend fun delete(id: Long)
|
||||||
|
|
||||||
suspend fun findAll(): List<User>
|
suspend fun findAll(): List<User>
|
||||||
|
|
||||||
suspend fun findAllByLimitAndByOffset(limit: Int, offset: Long = 0): List<UserEntity>
|
suspend fun findAllByLimitAndByOffset(limit: Int, offset: Long = 0): List<User>
|
||||||
|
|
||||||
suspend fun createFollower(id: Long, follower: Long)
|
suspend fun createFollower(id: Long, follower: Long)
|
||||||
suspend fun deleteFollower(id: Long, follower: Long)
|
suspend fun deleteFollower(id: Long, follower: Long)
|
||||||
suspend fun findFollowersById(id: Long): List<UserEntity>
|
suspend fun findFollowersById(id: Long): List<User>
|
||||||
|
|
||||||
|
suspend fun nextId():Long
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
package dev.usbharu.hideout.repository
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthentication
|
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthenticationEntity
|
|
||||||
import dev.usbharu.hideout.domain.model.UsersAuthentication
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import org.jetbrains.exposed.sql.*
|
|
||||||
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
|
||||||
|
|
||||||
class UserAuthRepository(private val database: Database) : IUserAuthRepository {
|
|
||||||
|
|
||||||
init {
|
|
||||||
transaction(database) {
|
|
||||||
SchemaUtils.create(UsersAuthentication)
|
|
||||||
SchemaUtils.createMissingTablesAndColumns(UsersAuthentication)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ResultRow.toUserAuth():UserAuthenticationEntity{
|
|
||||||
return UserAuthenticationEntity(
|
|
||||||
id = this[UsersAuthentication.id].value,
|
|
||||||
userId = this[UsersAuthentication.userId],
|
|
||||||
hash = this[UsersAuthentication.hash],
|
|
||||||
publicKey = this[UsersAuthentication.publicKey],
|
|
||||||
privateKey = this[UsersAuthentication.privateKey]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
suspend fun <T> query(block: suspend () -> T): T =
|
|
||||||
newSuspendedTransaction(Dispatchers.IO) {block()}
|
|
||||||
override suspend fun create(userAuthentication: UserAuthentication): UserAuthenticationEntity {
|
|
||||||
return query {
|
|
||||||
UserAuthenticationEntity(
|
|
||||||
UsersAuthentication.insert {
|
|
||||||
it[userId] = userAuthentication.userId
|
|
||||||
it[hash] = userAuthentication.hash
|
|
||||||
it[publicKey] = userAuthentication.publicKey
|
|
||||||
it[privateKey] = userAuthentication.privateKey
|
|
||||||
}[UsersAuthentication.id].value,userAuthentication
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findById(id: Long): UserAuthenticationEntity? {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByUserId(id:Long):UserAuthenticationEntity? {
|
|
||||||
return query {
|
|
||||||
UsersAuthentication.select { UsersAuthentication.userId eq id }.map { it.toUserAuth() }.singleOrNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun update(userAuthenticationEntity: UserAuthenticationEntity) {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun delete(id: Long) {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +1,17 @@
|
||||||
package dev.usbharu.hideout.repository
|
package dev.usbharu.hideout.repository
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.User
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
import dev.usbharu.hideout.domain.model.UserEntity
|
import dev.usbharu.hideout.service.IdGenerateService
|
||||||
import dev.usbharu.hideout.domain.model.Users
|
|
||||||
import dev.usbharu.hideout.domain.model.UsersFollowers
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import org.jetbrains.exposed.dao.id.LongIdTable
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
class UserRepository(private val database: Database) : IUserRepository {
|
class UserRepository(private val database: Database, private val idGenerateService: IdGenerateService) :
|
||||||
|
IUserRepository {
|
||||||
init {
|
init {
|
||||||
transaction(database) {
|
transaction(database) {
|
||||||
SchemaUtils.create(Users)
|
SchemaUtils.create(Users)
|
||||||
|
@ -20,45 +21,43 @@ class UserRepository(private val database: Database) : IUserRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ResultRow.toUser(): User {
|
|
||||||
return User(
|
|
||||||
this[Users.name],
|
|
||||||
this[Users.domain],
|
|
||||||
this[Users.screenName],
|
|
||||||
this[Users.description],
|
|
||||||
this[Users.inbox],
|
|
||||||
this[Users.outbox],
|
|
||||||
this[Users.url]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ResultRow.toUserEntity(): UserEntity {
|
|
||||||
return UserEntity(
|
|
||||||
this[Users.id].value,
|
|
||||||
this[Users.name],
|
|
||||||
this[Users.domain],
|
|
||||||
this[Users.screenName],
|
|
||||||
this[Users.description],
|
|
||||||
this[Users.inbox],
|
|
||||||
this[Users.outbox],
|
|
||||||
this[Users.url],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun <T> query(block: suspend () -> T): T =
|
suspend fun <T> query(block: suspend () -> T): T =
|
||||||
newSuspendedTransaction(Dispatchers.IO) { block() }
|
newSuspendedTransaction(Dispatchers.IO) { block() }
|
||||||
|
|
||||||
override suspend fun create(user: User): UserEntity {
|
override suspend fun save(user: User): User {
|
||||||
return query {
|
return query {
|
||||||
UserEntity(Users.insert {
|
val singleOrNull = Users.select { Users.id eq user.id }.singleOrNull()
|
||||||
|
if (singleOrNull == null) {
|
||||||
|
Users.insert {
|
||||||
|
it[id] = user.id
|
||||||
it[name] = user.name
|
it[name] = user.name
|
||||||
it[domain] = user.domain
|
it[domain] = user.domain
|
||||||
it[screenName] = user.screenName
|
it[screenName] = user.screenName
|
||||||
it[description] = user.description
|
it[description] = user.description
|
||||||
|
it[password] = user.password
|
||||||
it[inbox] = user.inbox
|
it[inbox] = user.inbox
|
||||||
it[outbox] = user.outbox
|
it[outbox] = user.outbox
|
||||||
it[url] = user.url
|
it[url] = user.url
|
||||||
}[Users.id].value, user)
|
it[createdAt] = user.createdAt.toEpochMilli()
|
||||||
|
it[publicKey] = user.publicKey
|
||||||
|
it[privateKey] = user.privateKey
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Users.update({ Users.id eq user.id }) {
|
||||||
|
it[name] = user.name
|
||||||
|
it[domain] = user.domain
|
||||||
|
it[screenName] = user.screenName
|
||||||
|
it[description] = user.description
|
||||||
|
it[password] = user.password
|
||||||
|
it[inbox] = user.inbox
|
||||||
|
it[outbox] = user.outbox
|
||||||
|
it[url] = user.url
|
||||||
|
it[createdAt] = user.createdAt.toEpochMilli()
|
||||||
|
it[publicKey] = user.publicKey
|
||||||
|
it[privateKey] = user.privateKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@query user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,59 +70,73 @@ class UserRepository(private val database: Database) : IUserRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findById(id: Long): UserEntity? {
|
override suspend fun findById(id: Long): User? {
|
||||||
return query {
|
return query {
|
||||||
Users.select { Users.id eq id }.map {
|
Users.select { Users.id eq id }.map {
|
||||||
it.toUserEntity()
|
it.toUser()
|
||||||
}.singleOrNull()
|
}.singleOrNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByIds(ids: List<Long>): List<UserEntity> {
|
override suspend fun findByIds(ids: List<Long>): List<User> {
|
||||||
return query {
|
return query {
|
||||||
Users.select { Users.id inList ids }.map {
|
Users.select { Users.id inList ids }.map {
|
||||||
it.toUserEntity()
|
it.toUser()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByName(name: String): UserEntity? {
|
override suspend fun findByName(name: String): List<User> {
|
||||||
return query {
|
return query {
|
||||||
Users.select { Users.name eq name }.map {
|
Users.select { Users.name eq name }.map {
|
||||||
it.toUserEntity()
|
it.toUser()
|
||||||
}.singleOrNull()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByNameAndDomains(names: List<Pair<String, String>>): List<UserEntity> {
|
override suspend fun findByNameAndDomain(name: String, domain: String): User? {
|
||||||
|
return query {
|
||||||
|
Users.select { Users.name eq name and (Users.domain eq domain) }.singleOrNull()?.toUser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findByDomain(domain: String): List<User> {
|
||||||
|
return query {
|
||||||
|
Users.select { Users.domain eq domain }.map {
|
||||||
|
it.toUser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findByNameAndDomains(names: List<Pair<String, String>>): List<User> {
|
||||||
return query {
|
return query {
|
||||||
val selectAll = Users.selectAll()
|
val selectAll = Users.selectAll()
|
||||||
names.forEach { (name, domain) ->
|
names.forEach { (name, domain) ->
|
||||||
selectAll.orWhere { Users.name eq name and (Users.domain eq domain) }
|
selectAll.orWhere { Users.name eq name and (Users.domain eq domain) }
|
||||||
}
|
}
|
||||||
selectAll.map { it.toUserEntity() }
|
selectAll.map { it.toUser() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByUrl(url: String): UserEntity? {
|
override suspend fun findByUrl(url: String): User? {
|
||||||
return query {
|
return query {
|
||||||
Users.select { Users.url eq url }.singleOrNull()?.toUserEntity()
|
Users.select { Users.url eq url }.singleOrNull()?.toUser()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByUrls(urls: List<String>): List<UserEntity> {
|
override suspend fun findByUrls(urls: List<String>): List<User> {
|
||||||
return query {
|
return query {
|
||||||
Users.select { Users.url inList urls }.map { it.toUserEntity() }
|
Users.select { Users.url inList urls }.map { it.toUser() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findFollowersById(id: Long): List<UserEntity> {
|
override suspend fun findFollowersById(id: Long): List<User> {
|
||||||
return query {
|
return query {
|
||||||
val followers = Users.alias("FOLLOWERS")
|
val followers = Users.alias("FOLLOWERS")
|
||||||
Users.innerJoin(
|
Users.innerJoin(
|
||||||
otherTable = UsersFollowers,
|
otherTable = UsersFollowers,
|
||||||
onColumn = { Users.id },
|
onColumn = { Users.id },
|
||||||
otherColumn = { UsersFollowers.userId })
|
otherColumn = { userId })
|
||||||
|
|
||||||
.innerJoin(
|
.innerJoin(
|
||||||
otherTable = followers,
|
otherTable = followers,
|
||||||
|
@ -136,41 +149,35 @@ class UserRepository(private val database: Database) : IUserRepository {
|
||||||
followers.get(Users.domain),
|
followers.get(Users.domain),
|
||||||
followers.get(Users.screenName),
|
followers.get(Users.screenName),
|
||||||
followers.get(Users.description),
|
followers.get(Users.description),
|
||||||
|
followers.get(Users.password),
|
||||||
followers.get(Users.inbox),
|
followers.get(Users.inbox),
|
||||||
followers.get(Users.outbox),
|
followers.get(Users.outbox),
|
||||||
followers.get(Users.url)
|
followers.get(Users.url),
|
||||||
|
followers.get(Users.publicKey),
|
||||||
|
followers.get(Users.privateKey),
|
||||||
|
followers.get(Users.createdAt)
|
||||||
)
|
)
|
||||||
.select { Users.id eq id }
|
.select { Users.id eq id }
|
||||||
.map {
|
.map {
|
||||||
UserEntity(
|
User(
|
||||||
id = it[followers[Users.id]].value,
|
id = it[followers[Users.id]],
|
||||||
name = it[followers[Users.name]],
|
name = it[followers[Users.name]],
|
||||||
domain = it[followers[Users.domain]],
|
domain = it[followers[Users.domain]],
|
||||||
screenName = it[followers[Users.screenName]],
|
screenName = it[followers[Users.screenName]],
|
||||||
description = it[followers[Users.description]],
|
description = it[followers[Users.description]],
|
||||||
|
password = it[followers[Users.password]],
|
||||||
inbox = it[followers[Users.inbox]],
|
inbox = it[followers[Users.inbox]],
|
||||||
outbox = it[followers[Users.outbox]],
|
outbox = it[followers[Users.outbox]],
|
||||||
url = it[followers[Users.url]],
|
url = it[followers[Users.url]],
|
||||||
|
publicKey = it[followers[Users.publicKey]],
|
||||||
|
privateKey = it[followers[Users.privateKey]],
|
||||||
|
createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override suspend fun update(userEntity: UserEntity) {
|
|
||||||
return query {
|
|
||||||
Users.update({ Users.id eq userEntity.id }) {
|
|
||||||
it[name] = userEntity.name
|
|
||||||
it[domain] = userEntity.domain
|
|
||||||
it[screenName] = userEntity.screenName
|
|
||||||
it[description] = userEntity.description
|
|
||||||
it[inbox] = userEntity.inbox
|
|
||||||
it[outbox] = userEntity.outbox
|
|
||||||
it[url] = userEntity.url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun delete(id: Long) {
|
override suspend fun delete(id: Long) {
|
||||||
query {
|
query {
|
||||||
Users.deleteWhere { Users.id.eq(id) }
|
Users.deleteWhere { Users.id.eq(id) }
|
||||||
|
@ -189,9 +196,60 @@ class UserRepository(private val database: Database) : IUserRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findAllByLimitAndByOffset(limit: Int, offset: Long): List<UserEntity> {
|
override suspend fun findAllByLimitAndByOffset(limit: Int, offset: Long): List<User> {
|
||||||
return query {
|
return query {
|
||||||
Users.selectAll().limit(limit, offset).map { it.toUserEntity() }
|
Users.selectAll().limit(limit, offset).map { it.toUser() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun nextId(): Long {
|
||||||
|
return idGenerateService.generateId()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Users : Table("users") {
|
||||||
|
val id = long("id")
|
||||||
|
val name = varchar("name", length = 64)
|
||||||
|
val domain = varchar("domain", length = 255)
|
||||||
|
val screenName = varchar("screen_name", length = 64)
|
||||||
|
val description = varchar("description", length = 600)
|
||||||
|
val password = varchar("password", length = 255).nullable()
|
||||||
|
val inbox = varchar("inbox", length = 255).uniqueIndex()
|
||||||
|
val outbox = varchar("outbox", length = 255).uniqueIndex()
|
||||||
|
val url = varchar("url", length = 255).uniqueIndex()
|
||||||
|
val publicKey = varchar("public_key", length = 10000)
|
||||||
|
val privateKey = varchar("private_key", length = 10000).nullable()
|
||||||
|
val createdAt = long("created_at")
|
||||||
|
|
||||||
|
override val primaryKey: PrimaryKey = PrimaryKey(id)
|
||||||
|
|
||||||
|
init {
|
||||||
|
uniqueIndex(name, domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ResultRow.toUser(): User {
|
||||||
|
return User(
|
||||||
|
this[Users.id],
|
||||||
|
this[Users.name],
|
||||||
|
this[Users.domain],
|
||||||
|
this[Users.screenName],
|
||||||
|
this[Users.description],
|
||||||
|
this[Users.password],
|
||||||
|
this[Users.inbox],
|
||||||
|
this[Users.outbox],
|
||||||
|
this[Users.url],
|
||||||
|
this[Users.publicKey],
|
||||||
|
this[Users.privateKey],
|
||||||
|
Instant.ofEpochMilli((this[Users.createdAt]))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
object UsersFollowers : LongIdTable("users_followers") {
|
||||||
|
val userId = long("user_id").references(Users.id).index()
|
||||||
|
val followerId = long("follower_id").references(Users.id)
|
||||||
|
|
||||||
|
init {
|
||||||
|
uniqueIndex(userId, followerId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
package dev.usbharu.hideout.routing
|
package dev.usbharu.hideout.routing
|
||||||
|
|
||||||
import dev.usbharu.hideout.plugins.UserSession
|
import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto
|
||||||
import dev.usbharu.hideout.service.IUserAuthService
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.auth.*
|
import io.ktor.server.auth.*
|
||||||
import io.ktor.server.request.*
|
import io.ktor.server.request.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
import io.ktor.server.sessions.*
|
|
||||||
|
|
||||||
fun Application.register(userAuthService: IUserAuthService) {
|
fun Application.register(userService: IUserService) {
|
||||||
|
|
||||||
routing {
|
routing {
|
||||||
get("/register") {
|
get("/register") {
|
||||||
|
@ -39,13 +38,10 @@ fun Application.register(userAuthService: IUserAuthService) {
|
||||||
val parameters = call.receiveParameters()
|
val parameters = call.receiveParameters()
|
||||||
val password = parameters["password"] ?: return@post call.respondRedirect("/register")
|
val password = parameters["password"] ?: return@post call.respondRedirect("/register")
|
||||||
val username = parameters["username"] ?: return@post call.respondRedirect("/register")
|
val username = parameters["username"] ?: return@post call.respondRedirect("/register")
|
||||||
if (userAuthService.usernameAlreadyUse(username)) {
|
if (userService.usernameAlreadyUse(username)) {
|
||||||
return@post call.respondRedirect("/register")
|
return@post call.respondRedirect("/register")
|
||||||
}
|
}
|
||||||
|
userService.createLocalUser(UserCreateDto(username, username, "", password))
|
||||||
val hash = userAuthService.hash(password)
|
|
||||||
userAuthService.registerAccount(username,hash)
|
|
||||||
// call.respondRedirect("/login")
|
|
||||||
call.respondRedirect("/users/$username")
|
call.respondRedirect("/users/$username")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package dev.usbharu.hideout.routing.activitypub
|
||||||
import dev.usbharu.hideout.exception.ParameterNotExistException
|
import dev.usbharu.hideout.exception.ParameterNotExistException
|
||||||
import dev.usbharu.hideout.plugins.respondAp
|
import dev.usbharu.hideout.plugins.respondAp
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||||
import dev.usbharu.hideout.util.HttpUtil.JsonLd
|
import dev.usbharu.hideout.util.HttpUtil.JsonLd
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
|
@ -12,9 +12,11 @@ import io.ktor.server.request.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
fun Routing.usersAP(activityPubUserService: ActivityPubUserService, userService: UserService) {
|
fun Routing.usersAP(activityPubUserService: ActivityPubUserService, userService: IUserService) {
|
||||||
route("/users/{name}") {
|
route("/users/{name}") {
|
||||||
createChild(ContentTypeRouteSelector(ContentType.Application.Activity, ContentType.Application.JsonLd)).handle {
|
createChild(ContentTypeRouteSelector(ContentType.Application.Activity, ContentType.Application.JsonLd)).handle {
|
||||||
|
call.application.log.debug("Signature: ${call.request.header("Signature")}")
|
||||||
|
call.application.log.debug("Authorization: ${call.request.header("Authorization")}")
|
||||||
val name =
|
val name =
|
||||||
call.parameters["name"] ?: throw ParameterNotExistException("Parameter(name='name') does not exist.")
|
call.parameters["name"] ?: throw ParameterNotExistException("Parameter(name='name') does not exist.")
|
||||||
val person = activityPubUserService.getPersonByName(name)
|
val person = activityPubUserService.getPersonByName(name)
|
||||||
|
@ -24,7 +26,7 @@ fun Routing.usersAP(activityPubUserService: ActivityPubUserService, userService:
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
get {
|
get {
|
||||||
val userEntity = userService.findByName(call.parameters["name"]!!)
|
val userEntity = userService.findByNameLocalUser(call.parameters["name"]!!)
|
||||||
call.respondText(userEntity.toString() + "\n" + userService.findFollowersById(userEntity.id))
|
call.respondText(userEntity.toString() + "\n" + userService.findFollowersById(userEntity.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,9 +35,11 @@ fun Routing.usersAP(activityPubUserService: ActivityPubUserService, userService:
|
||||||
class ContentTypeRouteSelector(private vararg val contentType: ContentType) : RouteSelector() {
|
class ContentTypeRouteSelector(private vararg val contentType: ContentType) : RouteSelector() {
|
||||||
override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation {
|
override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation {
|
||||||
|
|
||||||
val requestContentType =
|
context.call.application.log.debug("Accept: ${context.call.request.accept()}")
|
||||||
ContentType.parse(context.call.request.accept() ?: return RouteSelectorEvaluation.FailedParameter)
|
val requestContentType = context.call.request.accept() ?: return RouteSelectorEvaluation.FailedParameter
|
||||||
return if (contentType.any { contentType -> contentType.match(requestContentType) }) {
|
return if (requestContentType.split(",")
|
||||||
|
.find { contentType.find { contentType -> contentType.match(it) } != null } != null
|
||||||
|
) {
|
||||||
RouteSelectorEvaluation.Constant
|
RouteSelectorEvaluation.Constant
|
||||||
} else {
|
} else {
|
||||||
RouteSelectorEvaluation.FailedParameter
|
RouteSelectorEvaluation.FailedParameter
|
||||||
|
|
|
@ -4,14 +4,14 @@ import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.domain.model.wellknown.WebFinger
|
import dev.usbharu.hideout.domain.model.wellknown.WebFinger
|
||||||
import dev.usbharu.hideout.exception.IllegalParameterException
|
import dev.usbharu.hideout.exception.IllegalParameterException
|
||||||
import dev.usbharu.hideout.exception.ParameterNotExistException
|
import dev.usbharu.hideout.exception.ParameterNotExistException
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
fun Routing.webfinger(userService:UserService){
|
fun Routing.webfinger(userService: IUserService){
|
||||||
route("/.well-known/webfinger"){
|
route("/.well-known/webfinger"){
|
||||||
get {
|
get {
|
||||||
val acct = call.request.queryParameters["resource"]?.decodeURLPart()
|
val acct = call.request.queryParameters["resource"]?.decodeURLPart()
|
||||||
|
@ -25,7 +25,7 @@ fun Routing.webfinger(userService:UserService){
|
||||||
.substringAfter("acct:")
|
.substringAfter("acct:")
|
||||||
.trimStart('@')
|
.trimStart('@')
|
||||||
|
|
||||||
val userEntity = userService.findByName(accountName)
|
val userEntity = userService.findByNameLocalUser(accountName)
|
||||||
|
|
||||||
val webFinger = WebFinger(
|
val webFinger = WebFinger(
|
||||||
subject = acct,
|
subject = acct,
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
package dev.usbharu.hideout.service
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthentication
|
import java.security.KeyPair
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthenticationEntity
|
|
||||||
|
|
||||||
interface IUserAuthService {
|
interface IUserAuthService {
|
||||||
fun hash(password: String): String
|
fun hash(password: String): String
|
||||||
|
|
||||||
suspend fun usernameAlreadyUse(username: String): Boolean
|
suspend fun usernameAlreadyUse(username: String): Boolean
|
||||||
suspend fun registerAccount(username: String, hash: String)
|
|
||||||
|
suspend fun generateKeyPair():KeyPair
|
||||||
|
|
||||||
suspend fun verifyAccount(username: String, password: String): Boolean
|
suspend fun verifyAccount(username: String, password: String): Boolean
|
||||||
|
|
||||||
suspend fun findByUserId(userId: Long): UserAuthenticationEntity
|
|
||||||
|
|
||||||
suspend fun findByUsername(username: String): UserAuthenticationEntity
|
|
||||||
suspend fun createAccount(userEntity: UserAuthentication): UserAuthenticationEntity
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package dev.usbharu.hideout.service.activitypub
|
package dev.usbharu.hideout.service.activitypub
|
||||||
|
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import dev.usbharu.hideout.domain.model.ap.Accept
|
|
||||||
import dev.usbharu.hideout.domain.model.ap.Follow
|
|
||||||
import dev.usbharu.hideout.config.Config
|
import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.domain.model.ActivityPubResponse
|
import dev.usbharu.hideout.domain.model.ActivityPubResponse
|
||||||
import dev.usbharu.hideout.domain.model.ActivityPubStringResponse
|
import dev.usbharu.hideout.domain.model.ActivityPubStringResponse
|
||||||
|
import dev.usbharu.hideout.domain.model.ap.Accept
|
||||||
|
import dev.usbharu.hideout.domain.model.ap.Follow
|
||||||
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
||||||
import dev.usbharu.hideout.plugins.postAp
|
import dev.usbharu.hideout.plugins.postAp
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import dev.usbharu.hideout.service.job.JobQueueParentService
|
import dev.usbharu.hideout.service.job.JobQueueParentService
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
|
@ -17,7 +17,7 @@ import kjob.core.job.JobProps
|
||||||
class ActivityPubFollowServiceImpl(
|
class ActivityPubFollowServiceImpl(
|
||||||
private val jobQueueParentService: JobQueueParentService,
|
private val jobQueueParentService: JobQueueParentService,
|
||||||
private val activityPubUserService: ActivityPubUserService,
|
private val activityPubUserService: ActivityPubUserService,
|
||||||
private val userService: UserService,
|
private val userService: IUserService,
|
||||||
private val httpClient: HttpClient
|
private val httpClient: HttpClient
|
||||||
) : ActivityPubFollowService {
|
) : ActivityPubFollowService {
|
||||||
override suspend fun receiveFollow(follow: Follow): ActivityPubResponse {
|
override suspend fun receiveFollow(follow: Follow): ActivityPubResponse {
|
||||||
|
@ -32,9 +32,9 @@ class ActivityPubFollowServiceImpl(
|
||||||
|
|
||||||
override suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>) {
|
override suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>) {
|
||||||
val actor = props[ReceiveFollowJob.actor]
|
val actor = props[ReceiveFollowJob.actor]
|
||||||
val person = activityPubUserService.fetchPerson(actor)
|
|
||||||
val follow = Config.configData.objectMapper.readValue<Follow>(props[ReceiveFollowJob.follow])
|
|
||||||
val targetActor = props[ReceiveFollowJob.targetActor]
|
val targetActor = props[ReceiveFollowJob.targetActor]
|
||||||
|
val person = activityPubUserService.fetchPerson(actor,targetActor)
|
||||||
|
val follow = Config.configData.objectMapper.readValue<Follow>(props[ReceiveFollowJob.follow])
|
||||||
httpClient.postAp(
|
httpClient.postAp(
|
||||||
urlString = person.inbox ?: throw IllegalArgumentException("inbox is not found"),
|
urlString = person.inbox ?: throw IllegalArgumentException("inbox is not found"),
|
||||||
username = "$targetActor#pubkey",
|
username = "$targetActor#pubkey",
|
||||||
|
|
|
@ -7,7 +7,7 @@ import dev.usbharu.hideout.domain.model.ap.Create
|
||||||
import dev.usbharu.hideout.domain.model.ap.Note
|
import dev.usbharu.hideout.domain.model.ap.Note
|
||||||
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
||||||
import dev.usbharu.hideout.plugins.postAp
|
import dev.usbharu.hideout.plugins.postAp
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import dev.usbharu.hideout.service.job.JobQueueParentService
|
import dev.usbharu.hideout.service.job.JobQueueParentService
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import kjob.core.job.JobProps
|
import kjob.core.job.JobProps
|
||||||
|
@ -17,7 +17,7 @@ import java.time.Instant
|
||||||
class ActivityPubNoteServiceImpl(
|
class ActivityPubNoteServiceImpl(
|
||||||
private val httpClient: HttpClient,
|
private val httpClient: HttpClient,
|
||||||
private val jobQueueParentService: JobQueueParentService,
|
private val jobQueueParentService: JobQueueParentService,
|
||||||
private val userService: UserService
|
private val userService: IUserService
|
||||||
) : ActivityPubNoteService {
|
) : ActivityPubNoteService {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||||
|
@ -45,7 +45,7 @@ class ActivityPubNoteServiceImpl(
|
||||||
attributedTo = actor,
|
attributedTo = actor,
|
||||||
content = postEntity.text,
|
content = postEntity.text,
|
||||||
published = Instant.ofEpochMilli(postEntity.createdAt).toString(),
|
published = Instant.ofEpochMilli(postEntity.createdAt).toString(),
|
||||||
to = listOf("https://www.w3.org/ns/activitystreams#Public", actor + "/followers")
|
to = listOf("https://www.w3.org/ns/activitystreams#Public", actor + "/follower")
|
||||||
)
|
)
|
||||||
val inbox = props[DeliverPostJob.inbox]
|
val inbox = props[DeliverPostJob.inbox]
|
||||||
logger.debug("createNoteJob: actor={}, note={}, inbox={}", actor, postEntity, inbox)
|
logger.debug("createNoteJob: actor={}, note={}, inbox={}", actor, postEntity, inbox)
|
||||||
|
@ -54,7 +54,9 @@ class ActivityPubNoteServiceImpl(
|
||||||
username = "$actor#pubkey",
|
username = "$actor#pubkey",
|
||||||
jsonLd = Create(
|
jsonLd = Create(
|
||||||
name = "Create Note",
|
name = "Create Note",
|
||||||
`object` = note
|
`object` = note,
|
||||||
|
actor = note.attributedTo,
|
||||||
|
id = "${Config.configData.url}/create/note/${postEntity.id}"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ class ActivityPubServiceImpl(
|
||||||
return ActivityType.values().first { it.name.equals(type.asText(), true) }
|
return ActivityType.values().first { it.name.equals(type.asText(), true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse? {
|
override suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
ActivityType.Accept -> TODO()
|
ActivityType.Accept -> TODO()
|
||||||
ActivityType.Add -> TODO()
|
ActivityType.Add -> TODO()
|
||||||
|
|
|
@ -5,5 +5,5 @@ import dev.usbharu.hideout.domain.model.ap.Person
|
||||||
interface ActivityPubUserService {
|
interface ActivityPubUserService {
|
||||||
suspend fun getPersonByName(name:String): Person
|
suspend fun getPersonByName(name:String): Person
|
||||||
|
|
||||||
suspend fun fetchPerson(url:String): Person
|
suspend fun fetchPerson(url: String, targetActor: String? = null): Person
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
package dev.usbharu.hideout.service.activitypub
|
package dev.usbharu.hideout.service.activitypub
|
||||||
|
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.domain.model.ap.Image
|
import dev.usbharu.hideout.domain.model.ap.Image
|
||||||
import dev.usbharu.hideout.domain.model.ap.Key
|
import dev.usbharu.hideout.domain.model.ap.Key
|
||||||
import dev.usbharu.hideout.domain.model.ap.Person
|
import dev.usbharu.hideout.domain.model.ap.Person
|
||||||
import dev.usbharu.hideout.config.Config
|
import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto
|
||||||
import dev.usbharu.hideout.domain.model.User
|
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthentication
|
|
||||||
import dev.usbharu.hideout.exception.UserNotFoundException
|
import dev.usbharu.hideout.exception.UserNotFoundException
|
||||||
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
||||||
import dev.usbharu.hideout.service.IUserAuthService
|
import dev.usbharu.hideout.plugins.getAp
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
|
@ -19,8 +18,7 @@ import io.ktor.http.*
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
class ActivityPubUserServiceImpl(
|
class ActivityPubUserServiceImpl(
|
||||||
private val userService: UserService,
|
private val userService: IUserService,
|
||||||
private val userAuthService: IUserAuthService,
|
|
||||||
private val httpClient: HttpClient
|
private val httpClient: HttpClient
|
||||||
) :
|
) :
|
||||||
ActivityPubUserService {
|
ActivityPubUserService {
|
||||||
|
@ -28,8 +26,7 @@ class ActivityPubUserServiceImpl(
|
||||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||||
override suspend fun getPersonByName(name: String): Person {
|
override suspend fun getPersonByName(name: String): Person {
|
||||||
// TODO: JOINで書き直し
|
// TODO: JOINで書き直し
|
||||||
val userEntity = userService.findByName(name)
|
val userEntity = userService.findByNameLocalUser(name)
|
||||||
val userAuthEntity = userAuthService.findByUserId(userEntity.id)
|
|
||||||
val userUrl = "${Config.configData.url}/users/$name"
|
val userUrl = "${Config.configData.url}/users/$name"
|
||||||
return Person(
|
return Person(
|
||||||
type = emptyList(),
|
type = emptyList(),
|
||||||
|
@ -51,15 +48,14 @@ class ActivityPubUserServiceImpl(
|
||||||
name = "Public Key",
|
name = "Public Key",
|
||||||
id = "$userUrl#pubkey",
|
id = "$userUrl#pubkey",
|
||||||
owner = userUrl,
|
owner = userUrl,
|
||||||
publicKeyPem = userAuthEntity.publicKey
|
publicKeyPem = userEntity.publicKey
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchPerson(url: String): Person {
|
override suspend fun fetchPerson(url: String, targetActor: String?): Person {
|
||||||
return try {
|
return try {
|
||||||
val userEntity = userService.findByUrl(url)
|
val userEntity = userService.findByUrl(url)
|
||||||
val userAuthEntity = userAuthService.findByUsername(userEntity.name)
|
|
||||||
return Person(
|
return Person(
|
||||||
type = emptyList(),
|
type = emptyList(),
|
||||||
name = userEntity.name,
|
name = userEntity.name,
|
||||||
|
@ -80,33 +76,31 @@ class ActivityPubUserServiceImpl(
|
||||||
name = "Public Key",
|
name = "Public Key",
|
||||||
id = "$url#pubkey",
|
id = "$url#pubkey",
|
||||||
owner = url,
|
owner = url,
|
||||||
publicKeyPem = userAuthEntity.publicKey
|
publicKeyPem = userEntity.publicKey
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
} catch (e: UserNotFoundException) {
|
} catch (e: UserNotFoundException) {
|
||||||
val httpResponse = httpClient.get(url) {
|
val httpResponse = if (targetActor != null) {
|
||||||
|
httpClient.getAp(url,"$targetActor#pubkey")
|
||||||
|
}else {
|
||||||
|
httpClient.get(url) {
|
||||||
accept(ContentType.Application.Activity)
|
accept(ContentType.Application.Activity)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
val person = Config.configData.objectMapper.readValue<Person>(httpResponse.bodyAsText())
|
val person = Config.configData.objectMapper.readValue<Person>(httpResponse.bodyAsText())
|
||||||
val userEntity = userService.create(
|
|
||||||
User(
|
userService.createRemoteUser(
|
||||||
|
RemoteUserCreateDto(
|
||||||
name = person.preferredUsername
|
name = person.preferredUsername
|
||||||
?: throw IllegalActivityPubObjectException("preferredUsername is null"),
|
?: throw IllegalActivityPubObjectException("preferredUsername is null"),
|
||||||
domain = url.substringAfter(":").substringBeforeLast("/"),
|
domain = url.substringAfter("://").substringBeforeLast("/"),
|
||||||
screenName = (person.name ?: person.preferredUsername) ?: throw IllegalActivityPubObjectException("preferredUsername is null"),
|
screenName = (person.name ?: person.preferredUsername) ?: throw IllegalActivityPubObjectException("preferredUsername is null"),
|
||||||
description = person.summary ?: "",
|
description = person.summary ?: "",
|
||||||
inbox = person.inbox ?: throw IllegalActivityPubObjectException("inbox is null"),
|
inbox = person.inbox ?: throw IllegalActivityPubObjectException("inbox is null"),
|
||||||
outbox = person.outbox ?: throw IllegalActivityPubObjectException("outbox is null"),
|
outbox = person.outbox ?: throw IllegalActivityPubObjectException("outbox is null"),
|
||||||
url = url
|
url = url,
|
||||||
)
|
publicKey = person.publicKey?.publicKeyPem ?: throw IllegalActivityPubObjectException("publicKey is null"),
|
||||||
)
|
|
||||||
userAuthService.createAccount(
|
|
||||||
UserAuthentication(
|
|
||||||
userEntity.id,
|
|
||||||
null,
|
|
||||||
person.publicKey?.publicKeyPem ?: throw IllegalActivityPubObjectException("publicKey is null"),
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
person
|
person
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package dev.usbharu.hideout.service.impl
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
|
|
||||||
|
interface IUserService {
|
||||||
|
suspend fun findAll(limit: Int? = 100, offset: Long? = 0): List<User>
|
||||||
|
|
||||||
|
suspend fun findById(id: Long): User
|
||||||
|
|
||||||
|
suspend fun findByIds(ids: List<Long>): List<User>
|
||||||
|
|
||||||
|
suspend fun findByName(name: String): List<User>
|
||||||
|
|
||||||
|
suspend fun findByNameLocalUser(name: String): User
|
||||||
|
|
||||||
|
suspend fun findByNameAndDomains(names: List<Pair<String, String>>): List<User>
|
||||||
|
|
||||||
|
suspend fun findByUrl(url: String): User
|
||||||
|
|
||||||
|
suspend fun findByUrls(urls: List<String>): List<User>
|
||||||
|
|
||||||
|
suspend fun usernameAlreadyUse(username: String): Boolean
|
||||||
|
|
||||||
|
suspend fun createLocalUser(user: UserCreateDto): User
|
||||||
|
|
||||||
|
suspend fun createRemoteUser(user: RemoteUserCreateDto): User
|
||||||
|
|
||||||
|
suspend fun findFollowersById(id: Long): List<User>
|
||||||
|
|
||||||
|
suspend fun addFollowers(id: Long, follower: Long)
|
||||||
|
}
|
|
@ -1,23 +1,15 @@
|
||||||
package dev.usbharu.hideout.service.impl
|
package dev.usbharu.hideout.service.impl
|
||||||
|
|
||||||
import dev.usbharu.hideout.config.Config
|
import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.domain.model.User
|
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthentication
|
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthenticationEntity
|
|
||||||
import dev.usbharu.hideout.domain.model.UserEntity
|
|
||||||
import dev.usbharu.hideout.exception.UserNotFoundException
|
import dev.usbharu.hideout.exception.UserNotFoundException
|
||||||
import dev.usbharu.hideout.repository.IUserAuthRepository
|
|
||||||
import dev.usbharu.hideout.repository.IUserRepository
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
import dev.usbharu.hideout.service.IUserAuthService
|
import dev.usbharu.hideout.service.IUserAuthService
|
||||||
import io.ktor.util.*
|
import io.ktor.util.*
|
||||||
import java.security.*
|
import java.security.*
|
||||||
import java.security.interfaces.RSAPrivateKey
|
|
||||||
import java.security.interfaces.RSAPublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class UserAuthService(
|
class UserAuthService(
|
||||||
val userRepository: IUserRepository,
|
val userRepository: IUserRepository
|
||||||
val userAuthRepository: IUserAuthRepository
|
|
||||||
) : IUserAuthService {
|
) : IUserAuthService {
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,59 +23,15 @@ class UserAuthService(
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun registerAccount(username: String, hash: String) {
|
|
||||||
val url = "${Config.configData.url}/users/$username"
|
|
||||||
val registerUser = User(
|
|
||||||
name = username,
|
|
||||||
domain = Config.configData.domain,
|
|
||||||
screenName = username,
|
|
||||||
description = "",
|
|
||||||
inbox = "$url/inbox",
|
|
||||||
outbox = "$url/outbox",
|
|
||||||
url = url
|
|
||||||
)
|
|
||||||
val createdUser = userRepository.create(registerUser)
|
|
||||||
|
|
||||||
val keyPair = generateKeyPair()
|
|
||||||
val privateKey = keyPair.private as RSAPrivateKey
|
|
||||||
val publicKey = keyPair.public as RSAPublicKey
|
|
||||||
|
|
||||||
|
|
||||||
val userAuthentication = UserAuthentication(
|
|
||||||
createdUser.id,
|
|
||||||
hash,
|
|
||||||
publicKey.toPem(),
|
|
||||||
privateKey.toPem()
|
|
||||||
)
|
|
||||||
|
|
||||||
userAuthRepository.create(userAuthentication)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun verifyAccount(username: String, password: String): Boolean {
|
override suspend fun verifyAccount(username: String, password: String): Boolean {
|
||||||
val userEntity = userRepository.findByName(username)
|
val userEntity = userRepository.findByNameAndDomain(username, Config.configData.domain)
|
||||||
?: throw UserNotFoundException("$username was not found")
|
?: throw UserNotFoundException("$username was not found")
|
||||||
val userAuthEntity = userAuthRepository.findByUserId(userEntity.id)
|
return userEntity.password == hash(password)
|
||||||
?: throw UserNotFoundException("$username auth data was not found")
|
|
||||||
return userAuthEntity.hash == hash(password)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByUserId(userId: Long): UserAuthenticationEntity {
|
override suspend fun generateKeyPair(): KeyPair {
|
||||||
return userAuthRepository.findByUserId(userId) ?: throw UserNotFoundException("$userId was not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByUsername(username: String): UserAuthenticationEntity {
|
|
||||||
val userEntity = userRepository.findByName(username) ?: throw UserNotFoundException("$username was not found")
|
|
||||||
return userAuthRepository.findByUserId(userEntity.id)
|
|
||||||
?: throw UserNotFoundException("$username auth data was not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun createAccount(userEntity: UserAuthentication): UserAuthenticationEntity {
|
|
||||||
return userAuthRepository.create(userEntity)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateKeyPair(): KeyPair {
|
|
||||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||||
keyPairGenerator.initialize(1024)
|
keyPairGenerator.initialize(2048)
|
||||||
return keyPairGenerator.generateKeyPair()
|
return keyPairGenerator.generateKeyPair()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,14 +41,14 @@ class UserAuthService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun PublicKey.toPem(): String {
|
fun PublicKey.toPem(): String {
|
||||||
return "-----BEGIN PUBLIC KEY-----\n" +
|
return "-----BEGIN PUBLIC KEY-----\n" +
|
||||||
Base64.getEncoder().encodeToString(encoded).chunked(64).joinToString("\n") +
|
Base64.getEncoder().encodeToString(encoded).chunked(64).joinToString("\n") +
|
||||||
"\n-----END PUBLIC KEY-----\n"
|
"\n-----END PUBLIC KEY-----\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun PrivateKey.toPem(): String {
|
fun PrivateKey.toPem(): String {
|
||||||
return "-----BEGIN PRIVATE KEY-----" +
|
return "-----BEGIN PRIVATE KEY-----\n" +
|
||||||
Base64.getEncoder().encodeToString(encoded).chunked(64).joinToString("\n") +
|
Base64.getEncoder().encodeToString(encoded).chunked(64).joinToString("\n") +
|
||||||
"\n-----END PRIVATE KEY-----\n"
|
"\n-----END PRIVATE KEY-----\n"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
package dev.usbharu.hideout.service.impl
|
package dev.usbharu.hideout.service.impl
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.User
|
import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.domain.model.UserEntity
|
import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
import dev.usbharu.hideout.exception.UserNotFoundException
|
import dev.usbharu.hideout.exception.UserNotFoundException
|
||||||
import dev.usbharu.hideout.repository.IUserRepository
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
|
import dev.usbharu.hideout.service.IUserAuthService
|
||||||
import java.lang.Integer.min
|
import java.lang.Integer.min
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
class UserService(private val userRepository: IUserRepository) {
|
class UserService(private val userRepository: IUserRepository, private val userAuthService: IUserAuthService) :
|
||||||
|
IUserService {
|
||||||
|
|
||||||
private val maxLimit = 100
|
private val maxLimit = 100
|
||||||
suspend fun findAll(limit: Int? = maxLimit, offset: Long? = 0): List<UserEntity> {
|
override suspend fun findAll(limit: Int?, offset: Long?): List<User> {
|
||||||
|
|
||||||
return userRepository.findAllByLimitAndByOffset(
|
return userRepository.findAllByLimitAndByOffset(
|
||||||
min(limit ?: maxLimit, maxLimit),
|
min(limit ?: maxLimit, maxLimit),
|
||||||
|
@ -17,40 +22,83 @@ class UserService(private val userRepository: IUserRepository) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findById(id: Long): UserEntity {
|
override suspend fun findById(id: Long): User {
|
||||||
return userRepository.findById(id) ?: throw UserNotFoundException("$id was not found.")
|
return userRepository.findById(id) ?: throw UserNotFoundException("$id was not found.")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findByIds(ids: List<Long>): List<UserEntity> {
|
override suspend fun findByIds(ids: List<Long>): List<User> {
|
||||||
return userRepository.findByIds(ids)
|
return userRepository.findByIds(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findByName(name: String): UserEntity {
|
override suspend fun findByName(name: String): List<User> {
|
||||||
return userRepository.findByName(name)
|
return userRepository.findByName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findByNameLocalUser(name: String): User {
|
||||||
|
return userRepository.findByNameAndDomain(name, Config.configData.domain)
|
||||||
?: throw UserNotFoundException("$name was not found.")
|
?: throw UserNotFoundException("$name was not found.")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findByNameAndDomains(names: List<Pair<String,String>>): List<UserEntity> {
|
override suspend fun findByNameAndDomains(names: List<Pair<String, String>>): List<User> {
|
||||||
return userRepository.findByNameAndDomains(names)
|
return userRepository.findByNameAndDomains(names)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findByUrl(url: String): UserEntity {
|
override suspend fun findByUrl(url: String): User {
|
||||||
return userRepository.findByUrl(url) ?: throw UserNotFoundException("$url was not found.")
|
return userRepository.findByUrl(url) ?: throw UserNotFoundException("$url was not found.")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findByUrls(urls: List<String>): List<UserEntity> {
|
override suspend fun findByUrls(urls: List<String>): List<User> {
|
||||||
return userRepository.findByUrls(urls)
|
return userRepository.findByUrls(urls)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun create(user: User): UserEntity {
|
override suspend fun usernameAlreadyUse(username: String): Boolean {
|
||||||
return userRepository.create(user)
|
val findByNameAndDomain = userRepository.findByNameAndDomain(username, Config.configData.domain)
|
||||||
|
return findByNameAndDomain != null
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findFollowersById(id: Long): List<UserEntity> {
|
override suspend fun createLocalUser(user: UserCreateDto): User {
|
||||||
|
val nextId = userRepository.nextId()
|
||||||
|
val HashedPassword = userAuthService.hash(user.password)
|
||||||
|
val keyPair = userAuthService.generateKeyPair()
|
||||||
|
val userEntity = User(
|
||||||
|
id = nextId,
|
||||||
|
name = user.name,
|
||||||
|
domain = Config.configData.domain,
|
||||||
|
screenName = user.screenName,
|
||||||
|
description = user.description,
|
||||||
|
password = HashedPassword,
|
||||||
|
inbox = "${Config.configData.url}/users/${user.name}/inbox",
|
||||||
|
outbox = "${Config.configData.url}/users/${user.name}/outbox",
|
||||||
|
url = "${Config.configData.url}/users/${user.name}",
|
||||||
|
publicKey = keyPair.public.toPem(),
|
||||||
|
privateKey = keyPair.private.toPem(),
|
||||||
|
Instant.now()
|
||||||
|
)
|
||||||
|
return userRepository.save(userEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun createRemoteUser(user: RemoteUserCreateDto): User {
|
||||||
|
val nextId = userRepository.nextId()
|
||||||
|
val userEntity = User(
|
||||||
|
id = nextId,
|
||||||
|
name = user.name,
|
||||||
|
domain = user.domain,
|
||||||
|
screenName = user.screenName,
|
||||||
|
description = user.description,
|
||||||
|
inbox = user.inbox,
|
||||||
|
outbox = user.outbox,
|
||||||
|
url = user.url,
|
||||||
|
publicKey = user.publicKey,
|
||||||
|
createdAt = Instant.now()
|
||||||
|
)
|
||||||
|
return userRepository.save(userEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findFollowersById(id: Long): List<User> {
|
||||||
return userRepository.findFollowersById(id)
|
return userRepository.findFollowersById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun addFollowers(id: Long, follower: Long) {
|
override suspend fun addFollowers(id: Long, follower: Long) {
|
||||||
return userRepository.createFollower(id, follower)
|
return userRepository.createFollower(id, follower)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
package dev.usbharu.hideout.service.signature
|
package dev.usbharu.hideout.service.signature
|
||||||
|
|
||||||
import dev.usbharu.hideout.plugins.KtorKeyMap
|
import dev.usbharu.hideout.plugins.KtorKeyMap
|
||||||
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
import dev.usbharu.hideout.service.IUserAuthService
|
import dev.usbharu.hideout.service.IUserAuthService
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import tech.barbero.http.message.signing.HttpMessage
|
import tech.barbero.http.message.signing.HttpMessage
|
||||||
import tech.barbero.http.message.signing.SignatureHeaderVerifier
|
import tech.barbero.http.message.signing.SignatureHeaderVerifier
|
||||||
|
|
||||||
class HttpSignatureVerifyServiceImpl(private val userAuthService: IUserAuthService) : HttpSignatureVerifyService {
|
class HttpSignatureVerifyServiceImpl(private val userAuthService: IUserRepository) : HttpSignatureVerifyService {
|
||||||
override fun verify(headers: Headers): Boolean {
|
override fun verify(headers: Headers): Boolean {
|
||||||
val build = SignatureHeaderVerifier.builder().keyMap(KtorKeyMap(userAuthService)).build()
|
val build = SignatureHeaderVerifier.builder().keyMap(KtorKeyMap(userAuthService)).build()
|
||||||
return true;
|
return true
|
||||||
build.verify(object : HttpMessage {
|
build.verify(object : HttpMessage {
|
||||||
override fun headerValues(name: String?): MutableList<String> {
|
override fun headerValues(name: String?): MutableList<String> {
|
||||||
return name?.let { headers.getAll(it) }?.toMutableList() ?: mutableListOf()
|
return name?.let { headers.getAll(it) }?.toMutableList() ?: mutableListOf()
|
||||||
|
|
|
@ -30,6 +30,10 @@ object HttpUtil {
|
||||||
get() = ContentType("application", "activity+json")
|
get() = ContentType("application", "activity+json")
|
||||||
|
|
||||||
val ContentType.Application.JsonLd: ContentType
|
val ContentType.Application.JsonLd: ContentType
|
||||||
get() = ContentType("application", "ld+json", listOf(HeaderValueParam("profile", "https://www.w3.org/ns/activitystreams")))
|
get() = ContentType(
|
||||||
|
"application",
|
||||||
|
"ld+json",
|
||||||
|
listOf(HeaderValueParam("profile", "https://www.w3.org/ns/activitystreams"))
|
||||||
|
)
|
||||||
// fun
|
// fun
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import org.jetbrains.exposed.dao.id.LongIdTable
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.isNull
|
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.plus
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.plus
|
||||||
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta content="#000000" name="theme-color"/>
|
||||||
<title>Solid App</title>
|
<title>Solid App</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
package dev.usbharu.hideout.plugins
|
package dev.usbharu.hideout.plugins
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.ap.JsonLd
|
import dev.usbharu.hideout.domain.model.ap.JsonLd
|
||||||
import dev.usbharu.hideout.domain.model.User
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthentication
|
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthenticationEntity
|
|
||||||
import dev.usbharu.hideout.domain.model.UserEntity
|
|
||||||
import dev.usbharu.hideout.repository.IUserAuthRepository
|
|
||||||
import dev.usbharu.hideout.repository.IUserRepository
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
import dev.usbharu.hideout.service.impl.UserAuthService
|
|
||||||
import dev.usbharu.hideout.service.impl.toPem
|
import dev.usbharu.hideout.service.impl.toPem
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.engine.mock.*
|
import io.ktor.client.engine.mock.*
|
||||||
|
@ -15,43 +10,62 @@ import io.ktor.client.plugins.logging.*
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import java.security.KeyPairGenerator
|
import java.security.KeyPairGenerator
|
||||||
import java.security.interfaces.RSAPrivateKey
|
import java.time.Instant
|
||||||
import java.security.interfaces.RSAPublicKey
|
|
||||||
|
|
||||||
class ActivityPubKtTest {
|
class ActivityPubKtTest {
|
||||||
@Test
|
@Test
|
||||||
fun HttpSignTest(): Unit = runBlocking {
|
fun HttpSignTest(): Unit = runBlocking {
|
||||||
|
|
||||||
val ktorKeyMap = KtorKeyMap(UserAuthService(object : IUserRepository {
|
val ktorKeyMap = KtorKeyMap(object : IUserRepository {
|
||||||
override suspend fun create(user: User): UserEntity {
|
override suspend fun save(user: User): User {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findById(id: Long): UserEntity? {
|
override suspend fun findById(id: Long): User? {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByIds(ids: List<Long>): List<UserEntity> {
|
override suspend fun findByIds(ids: List<Long>): List<User> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByName(name: String): UserEntity? {
|
override suspend fun findByName(name: String): List<User> {
|
||||||
return UserEntity(1, "test", "localhost", "test", "","","","")
|
TODO()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByNameAndDomains(names: List<Pair<String, String>>): List<UserEntity> {
|
override suspend fun findByNameAndDomain(name: String, domain: String): User? {
|
||||||
|
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||||
|
keyPairGenerator.initialize(1024)
|
||||||
|
val generateKeyPair = keyPairGenerator.generateKeyPair()
|
||||||
|
return User(
|
||||||
|
1,
|
||||||
|
"test",
|
||||||
|
"localhost",
|
||||||
|
"test",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
generateKeyPair.private.toPem(),
|
||||||
|
Instant.now()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findByDomain(domain: String): List<User> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByUrl(url: String): UserEntity? {
|
override suspend fun findByNameAndDomains(names: List<Pair<String, String>>): List<User> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByUrls(urls: List<String>): List<UserEntity> {
|
override suspend fun findByUrl(url: String): User? {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun update(userEntity: UserEntity) {
|
override suspend fun findByUrls(urls: List<String>): List<User> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +77,7 @@ class ActivityPubKtTest {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findAllByLimitAndByOffset(limit: Int, offset: Long): List<UserEntity> {
|
override suspend fun findAllByLimitAndByOffset(limit: Int, offset: Long): List<User> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,37 +89,15 @@ class ActivityPubKtTest {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findFollowersById(id: Long): List<UserEntity> {
|
override suspend fun findFollowersById(id: Long): List<User> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
}, object : IUserAuthRepository {
|
override suspend fun nextId(): Long {
|
||||||
override suspend fun create(userAuthentication: UserAuthentication): UserAuthenticationEntity {
|
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findById(id: Long): UserAuthenticationEntity? {
|
})
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun update(userAuthenticationEntity: UserAuthenticationEntity) {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun delete(id: Long) {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByUserId(id: Long): UserAuthenticationEntity? {
|
|
||||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
|
||||||
keyPairGenerator.initialize(1024)
|
|
||||||
val generateKeyPair = keyPairGenerator.generateKeyPair()
|
|
||||||
return UserAuthenticationEntity(
|
|
||||||
1, 1, "test", (generateKeyPair.public as RSAPublicKey).toPem(),
|
|
||||||
(generateKeyPair.private as RSAPrivateKey).toPem()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
val httpClient = HttpClient(MockEngine { httpRequestData ->
|
val httpClient = HttpClient(MockEngine { httpRequestData ->
|
||||||
respondOk()
|
respondOk()
|
||||||
|
|
|
@ -1,52 +1,67 @@
|
||||||
package dev.usbharu.hideout.plugins
|
package dev.usbharu.hideout.plugins
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.User
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthentication
|
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthenticationEntity
|
|
||||||
import dev.usbharu.hideout.domain.model.UserEntity
|
|
||||||
import dev.usbharu.hideout.repository.IUserAuthRepository
|
|
||||||
import dev.usbharu.hideout.repository.IUserRepository
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
import dev.usbharu.hideout.service.impl.UserAuthService
|
|
||||||
import dev.usbharu.hideout.service.impl.toPem
|
import dev.usbharu.hideout.service.impl.toPem
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import java.security.KeyPairGenerator
|
import java.security.KeyPairGenerator
|
||||||
import java.security.interfaces.RSAPrivateKey
|
import java.time.Instant
|
||||||
import java.security.interfaces.RSAPublicKey
|
|
||||||
|
|
||||||
class KtorKeyMapTest {
|
class KtorKeyMapTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getPrivateKey() {
|
fun getPrivateKey() {
|
||||||
val ktorKeyMap = KtorKeyMap(UserAuthService(object : IUserRepository {
|
val ktorKeyMap = KtorKeyMap(object : IUserRepository {
|
||||||
override suspend fun create(user: User): UserEntity {
|
override suspend fun save(user: User): User {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findById(id: Long): UserEntity? {
|
override suspend fun findById(id: Long): User? {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByIds(ids: List<Long>): List<UserEntity> {
|
override suspend fun findByIds(ids: List<Long>): List<User> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByName(name: String): UserEntity? {
|
override suspend fun findByName(name: String): List<User> {
|
||||||
return UserEntity(1, "test", "localhost", "test", "","","","")
|
TODO()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByNameAndDomains(names: List<Pair<String, String>>): List<UserEntity> {
|
override suspend fun findByNameAndDomain(name: String, domain: String): User? {
|
||||||
|
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||||
|
keyPairGenerator.initialize(1024)
|
||||||
|
val generateKeyPair = keyPairGenerator.generateKeyPair()
|
||||||
|
return User(
|
||||||
|
1,
|
||||||
|
"test",
|
||||||
|
"localhost",
|
||||||
|
"test",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
generateKeyPair.private.toPem(),
|
||||||
|
createdAt = Instant.now()
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findByDomain(domain: String): List<User> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByUrl(url: String): UserEntity? {
|
override suspend fun findByNameAndDomains(names: List<Pair<String, String>>): List<User> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByUrls(urls: List<String>): List<UserEntity> {
|
override suspend fun findByUrl(url: String): User? {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun update(userEntity: UserEntity) {
|
override suspend fun findByUrls(urls: List<String>): List<User> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +73,7 @@ class KtorKeyMapTest {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findAllByLimitAndByOffset(limit: Int, offset: Long): List<UserEntity> {
|
override suspend fun findAllByLimitAndByOffset(limit: Int, offset: Long): List<User> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,37 +85,15 @@ class KtorKeyMapTest {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findFollowersById(id: Long): List<UserEntity> {
|
override suspend fun findFollowersById(id: Long): List<User> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
}, object : IUserAuthRepository {
|
override suspend fun nextId(): Long {
|
||||||
override suspend fun create(userAuthentication: UserAuthentication): UserAuthenticationEntity {
|
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findById(id: Long): UserAuthenticationEntity? {
|
})
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun update(userAuthenticationEntity: UserAuthenticationEntity) {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun delete(id: Long) {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByUserId(id: Long): UserAuthenticationEntity? {
|
|
||||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
|
||||||
keyPairGenerator.initialize(1024)
|
|
||||||
val generateKeyPair = keyPairGenerator.generateKeyPair()
|
|
||||||
return UserAuthenticationEntity(
|
|
||||||
1, 1, "test", (generateKeyPair.public as RSAPublicKey).toPem(),
|
|
||||||
(generateKeyPair.private as RSAPrivateKey).toPem()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
ktorKeyMap.getPrivateKey("test")
|
ktorKeyMap.getPrivateKey("test")
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.repository
|
package dev.usbharu.hideout.repository
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.User
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
import dev.usbharu.hideout.domain.model.Users
|
import dev.usbharu.hideout.service.IdGenerateService
|
||||||
import dev.usbharu.hideout.domain.model.UsersFollowers
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.jetbrains.exposed.sql.Database
|
import org.jetbrains.exposed.sql.Database
|
||||||
|
@ -15,6 +14,9 @@ import org.junit.jupiter.api.AfterEach
|
||||||
import org.junit.jupiter.api.Assertions.assertIterableEquals
|
import org.junit.jupiter.api.Assertions.assertIterableEquals
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import java.time.Clock
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.ZoneId
|
||||||
|
|
||||||
|
|
||||||
class UserRepositoryTest {
|
class UserRepositoryTest {
|
||||||
|
@ -41,38 +43,54 @@ class UserRepositoryTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `findFollowersById フォロワー一覧を取得`() = runTest {
|
fun `findFollowersById フォロワー一覧を取得`() = runTest {
|
||||||
val userRepository = UserRepository(db)
|
val userRepository = UserRepository(db, object : IdGenerateService {
|
||||||
val user = userRepository.create(
|
override suspend fun generateId(): Long {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
val user = userRepository.save(
|
||||||
User(
|
User(
|
||||||
"test",
|
id = 0L,
|
||||||
"example.com",
|
name = "test",
|
||||||
"testUser",
|
domain = "example.com",
|
||||||
"This user is test user.",
|
screenName = "testUser",
|
||||||
"https://example.com/inbox",
|
description = "This user is test user.",
|
||||||
"https://example.com/outbox",
|
password = "https://example.com/inbox",
|
||||||
"https://example.com"
|
inbox = "",
|
||||||
|
outbox = "https://example.com/outbox",
|
||||||
|
url = "https://example.com",
|
||||||
|
publicKey = "",
|
||||||
|
createdAt = Instant.now(Clock.tickMillis(ZoneId.systemDefault()))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val follower = userRepository.create(
|
val follower = userRepository.save(
|
||||||
User(
|
User(
|
||||||
"follower",
|
id = 1L,
|
||||||
"follower.example.com",
|
name = "follower",
|
||||||
"followerUser",
|
domain = "follower.example.com",
|
||||||
"This user is follower user.",
|
screenName = "followerUser",
|
||||||
"https://follower.example.com/inbox",
|
description = "This user is follower user.",
|
||||||
"https://follower.example.com/outbox",
|
password = "",
|
||||||
"https://follower.example.com"
|
inbox = "https://follower.example.com/inbox",
|
||||||
|
outbox = "https://follower.example.com/outbox",
|
||||||
|
url = "https://follower.example.com",
|
||||||
|
publicKey = "",
|
||||||
|
createdAt = Instant.now(Clock.tickMillis(ZoneId.systemDefault()))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val follower2 = userRepository.create(
|
val follower2 = userRepository.save(
|
||||||
User(
|
User(
|
||||||
"follower2",
|
id = 3L,
|
||||||
"follower2.example.com",
|
name = "follower2",
|
||||||
"followerUser2",
|
domain = "follower2.example.com",
|
||||||
"This user is follower user 2.",
|
screenName = "followerUser2",
|
||||||
"https://follower2.example.com/inbox",
|
description = "This user is follower user 2.",
|
||||||
"https://follower2.example.com/outbox",
|
password = "",
|
||||||
"https://follower2.example.com"
|
inbox = "https://follower2.example.com/inbox",
|
||||||
|
outbox = "https://follower2.example.com/outbox",
|
||||||
|
url = "https://follower2.example.com",
|
||||||
|
publicKey = "",
|
||||||
|
createdAt = Instant.now(Clock.tickMillis(ZoneId.systemDefault()))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
userRepository.createFollower(user.id, follower.id)
|
userRepository.createFollower(user.id, follower.id)
|
||||||
|
@ -85,27 +103,37 @@ class UserRepositoryTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `createFollower フォロワー追加`() = runTest {
|
fun `createFollower フォロワー追加`() = runTest {
|
||||||
val userRepository = UserRepository(db)
|
val userRepository = UserRepository(db, object : IdGenerateService {
|
||||||
val user = userRepository.create(
|
override suspend fun generateId(): Long {
|
||||||
User(
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
val user = userRepository.save(
|
||||||
|
User(0L,
|
||||||
"test",
|
"test",
|
||||||
"example.com",
|
"example.com",
|
||||||
"testUser",
|
"testUser",
|
||||||
"This user is test user.",
|
"This user is test user.",
|
||||||
"https://example.com/inbox",
|
"https://example.com/inbox",
|
||||||
|
"",
|
||||||
"https://example.com/outbox",
|
"https://example.com/outbox",
|
||||||
"https://example.com"
|
"https://example.com",
|
||||||
|
publicKey = "",
|
||||||
|
createdAt = Instant.now()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val follower = userRepository.create(
|
val follower = userRepository.save(
|
||||||
User(
|
User(1L,
|
||||||
"follower",
|
"follower",
|
||||||
"follower.example.com",
|
"follower.example.com",
|
||||||
"followerUser",
|
"followerUser",
|
||||||
"This user is follower user.",
|
"This user is follower user.",
|
||||||
|
"",
|
||||||
"https://follower.example.com/inbox",
|
"https://follower.example.com/inbox",
|
||||||
"https://follower.example.com/outbox",
|
"https://follower.example.com/outbox",
|
||||||
"https://follower.example.com"
|
"https://follower.example.com",
|
||||||
|
publicKey = "",
|
||||||
|
createdAt = Instant.now()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
userRepository.createFollower(user.id, follower.id)
|
userRepository.createFollower(user.id, follower.id)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import dev.usbharu.hideout.plugins.configureSerialization
|
||||||
import dev.usbharu.hideout.plugins.configureStatusPages
|
import dev.usbharu.hideout.plugins.configureStatusPages
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
||||||
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.UserService
|
||||||
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
|
@ -45,7 +46,7 @@ class InboxRoutingKtTest {
|
||||||
val activityPubService = mock<ActivityPubService>{
|
val activityPubService = mock<ActivityPubService>{
|
||||||
on { parseActivity(any()) } doThrow JsonParseException()
|
on { parseActivity(any()) } doThrow JsonParseException()
|
||||||
}
|
}
|
||||||
val userService = mock<UserService>()
|
val userService = mock<IUserService>()
|
||||||
val activityPubUserService = mock<ActivityPubUserService>()
|
val activityPubUserService = mock<ActivityPubUserService>()
|
||||||
application {
|
application {
|
||||||
configureStatusPages()
|
configureStatusPages()
|
||||||
|
@ -82,7 +83,7 @@ class InboxRoutingKtTest {
|
||||||
val activityPubService = mock<ActivityPubService>{
|
val activityPubService = mock<ActivityPubService>{
|
||||||
on { parseActivity(any()) } doThrow JsonParseException()
|
on { parseActivity(any()) } doThrow JsonParseException()
|
||||||
}
|
}
|
||||||
val userService = mock<UserService>()
|
val userService = mock<IUserService>()
|
||||||
val activityPubUserService = mock<ActivityPubUserService>()
|
val activityPubUserService = mock<ActivityPubUserService>()
|
||||||
application {
|
application {
|
||||||
configureStatusPages()
|
configureStatusPages()
|
||||||
|
|
|
@ -9,13 +9,15 @@ import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import dev.usbharu.hideout.domain.model.ap.Image
|
import dev.usbharu.hideout.domain.model.ap.Image
|
||||||
import dev.usbharu.hideout.domain.model.ap.Key
|
import dev.usbharu.hideout.domain.model.ap.Key
|
||||||
import dev.usbharu.hideout.domain.model.ap.Person
|
import dev.usbharu.hideout.domain.model.ap.Person
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
import dev.usbharu.hideout.plugins.configureRouting
|
import dev.usbharu.hideout.plugins.configureRouting
|
||||||
import dev.usbharu.hideout.plugins.configureSerialization
|
import dev.usbharu.hideout.plugins.configureSerialization
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
||||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||||
|
import dev.usbharu.hideout.util.HttpUtil.JsonLd
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.ktor.client.statement.*
|
import io.ktor.client.statement.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
|
@ -24,8 +26,11 @@ import io.ktor.server.testing.*
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.mockito.ArgumentMatchers.anyString
|
import org.mockito.ArgumentMatchers.anyString
|
||||||
import org.mockito.kotlin.doReturn
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.eq
|
||||||
import org.mockito.kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
|
import java.time.Instant
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
|
||||||
class UsersAPTest {
|
class UsersAPTest {
|
||||||
|
@ -62,7 +67,7 @@ class UsersAPTest {
|
||||||
|
|
||||||
val httpSignatureVerifyService = mock<HttpSignatureVerifyService> {}
|
val httpSignatureVerifyService = mock<HttpSignatureVerifyService> {}
|
||||||
val activityPubService = mock<ActivityPubService> {}
|
val activityPubService = mock<ActivityPubService> {}
|
||||||
val userService = mock<UserService> {}
|
val userService = mock<IUserService> {}
|
||||||
|
|
||||||
val activityPubUserService = mock<ActivityPubUserService> {
|
val activityPubUserService = mock<ActivityPubUserService> {
|
||||||
onBlocking { getPersonByName(anyString()) } doReturn person
|
onBlocking { getPersonByName(anyString()) } doReturn person
|
||||||
|
@ -70,7 +75,13 @@ class UsersAPTest {
|
||||||
|
|
||||||
application {
|
application {
|
||||||
configureSerialization()
|
configureSerialization()
|
||||||
configureRouting(httpSignatureVerifyService, activityPubService, userService, activityPubUserService,mock())
|
configureRouting(
|
||||||
|
httpSignatureVerifyService,
|
||||||
|
activityPubService,
|
||||||
|
userService,
|
||||||
|
activityPubUserService,
|
||||||
|
mock()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
client.get("/users/test") {
|
client.get("/users/test") {
|
||||||
accept(ContentType.Application.Activity)
|
accept(ContentType.Application.Activity)
|
||||||
|
@ -88,4 +99,121 @@ class UsersAPTest {
|
||||||
assertEquals(person, readValue)
|
assertEquals(person, readValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test()
|
||||||
|
// @Disabled
|
||||||
|
fun `ユーザのURLにAcceptヘッダーをActivityとJson-LDにしてアクセスしたときPersonが返ってくる`() = testApplication {
|
||||||
|
environment {
|
||||||
|
config = ApplicationConfig("empty.conf")
|
||||||
|
}
|
||||||
|
val person = Person(
|
||||||
|
type = emptyList(),
|
||||||
|
name = "test",
|
||||||
|
id = "http://example.com/users/test",
|
||||||
|
preferredUsername = "test",
|
||||||
|
summary = "test user",
|
||||||
|
inbox = "http://example.com/users/test/inbox",
|
||||||
|
outbox = "http://example.com/users/test/outbox",
|
||||||
|
url = "http://example.com/users/test",
|
||||||
|
icon = Image(
|
||||||
|
type = emptyList(),
|
||||||
|
name = "http://example.com/users/test/icon.png",
|
||||||
|
mediaType = "image/png",
|
||||||
|
url = "http://example.com/users/test/icon.png"
|
||||||
|
),
|
||||||
|
publicKey = Key(
|
||||||
|
type = emptyList(),
|
||||||
|
name = "Public Key",
|
||||||
|
id = "http://example.com/users/test#pubkey",
|
||||||
|
owner = "https://example.com/users/test",
|
||||||
|
publicKeyPem = "-----BEGIN PUBLIC KEY-----\n\n-----END PUBLIC KEY-----"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
person.context = listOf("https://www.w3.org/ns/activitystreams")
|
||||||
|
|
||||||
|
val httpSignatureVerifyService = mock<HttpSignatureVerifyService> {}
|
||||||
|
val activityPubService = mock<ActivityPubService> {}
|
||||||
|
val userService = mock<IUserService> {}
|
||||||
|
|
||||||
|
val activityPubUserService = mock<ActivityPubUserService> {
|
||||||
|
onBlocking { getPersonByName(anyString()) } doReturn person
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
configureSerialization()
|
||||||
|
configureRouting(
|
||||||
|
httpSignatureVerifyService,
|
||||||
|
activityPubService,
|
||||||
|
userService,
|
||||||
|
activityPubUserService,
|
||||||
|
mock()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
client.get("/users/test") {
|
||||||
|
accept(ContentType.Application.JsonLd)
|
||||||
|
accept(ContentType.Application.Activity)
|
||||||
|
}.let {
|
||||||
|
val objectMapper = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||||
|
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
|
||||||
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||||
|
objectMapper.configOverride(List::class.java).setSetterInfo(
|
||||||
|
JsonSetter.Value.forValueNulls(
|
||||||
|
Nulls.AS_EMPTY
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val actual = it.bodyAsText()
|
||||||
|
val readValue = objectMapper.readValue<Person>(actual)
|
||||||
|
assertEquals(person, readValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
// @Disabled
|
||||||
|
fun contentType_Test() {
|
||||||
|
|
||||||
|
assertTrue(ContentType.Application.Activity.match("application/activity+json"))
|
||||||
|
val listOf = listOf(ContentType.Application.JsonLd, ContentType.Application.Activity)
|
||||||
|
assertTrue(listOf.find { contentType ->
|
||||||
|
contentType.match("application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
|
||||||
|
}.let { it != null })
|
||||||
|
assertTrue(ContentType.Application.JsonLd.match("application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun ユーザーのURLにAcceptヘッダーをhtmlにしてアクセスしたときはただの文字を返す() = testApplication {
|
||||||
|
environment {
|
||||||
|
config = ApplicationConfig("empty.conf")
|
||||||
|
}
|
||||||
|
val userService = mock<IUserService> {
|
||||||
|
onBlocking { findByNameLocalUser(eq("test")) } doReturn User(
|
||||||
|
1L,
|
||||||
|
"test",
|
||||||
|
"example.com",
|
||||||
|
"test",
|
||||||
|
"",
|
||||||
|
"hashedPassword",
|
||||||
|
"https://example.com/inbox",
|
||||||
|
"https://example.com/outbox",
|
||||||
|
"https://example.com",
|
||||||
|
"-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----",
|
||||||
|
"-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----",
|
||||||
|
Instant.now()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
application {
|
||||||
|
configureRouting(
|
||||||
|
mock(),
|
||||||
|
mock(),
|
||||||
|
userService,
|
||||||
|
mock(),
|
||||||
|
mock()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
client.get("/users/test") {
|
||||||
|
accept(ContentType.Text.Html)
|
||||||
|
}.let {
|
||||||
|
assertEquals(HttpStatusCode.OK, it.status)
|
||||||
|
assertTrue(it.contentType()?.match(ContentType.Text.Plain) == true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,10 @@ package dev.usbharu.hideout.service.activitypub
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import dev.usbharu.hideout.config.Config
|
import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.config.ConfigData
|
import dev.usbharu.hideout.config.ConfigData
|
||||||
import dev.usbharu.hideout.domain.model.UserEntity
|
|
||||||
import dev.usbharu.hideout.domain.model.ap.*
|
import dev.usbharu.hideout.domain.model.ap.*
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import dev.usbharu.hideout.service.job.JobQueueParentService
|
import dev.usbharu.hideout.service.job.JobQueueParentService
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.engine.mock.*
|
import io.ktor.client.engine.mock.*
|
||||||
|
@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test
|
||||||
import org.mockito.ArgumentMatchers.anyString
|
import org.mockito.ArgumentMatchers.anyString
|
||||||
import org.mockito.kotlin.*
|
import org.mockito.kotlin.*
|
||||||
import utils.JsonObjectMapper
|
import utils.JsonObjectMapper
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
class ActivityPubFollowServiceImplTest {
|
class ActivityPubFollowServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
|
@ -49,8 +50,9 @@ class ActivityPubFollowServiceImplTest {
|
||||||
val follow = scheduleContext.props.props[ReceiveFollowJob.follow.name]
|
val follow = scheduleContext.props.props[ReceiveFollowJob.follow.name]
|
||||||
assertEquals("https://follower.example.com", actor)
|
assertEquals("https://follower.example.com", actor)
|
||||||
assertEquals("https://example.com", targetActor)
|
assertEquals("https://example.com", targetActor)
|
||||||
|
//language=JSON
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"""{"type":"Follow","name":"Follow","object":"https://example.com","actor":"https://follower.example.com","@context":null}""",
|
"""{"type":"Follow","name":"Follow","actor":"https://follower.example.com","object":"https://example.com","@context":null}""",
|
||||||
follow
|
follow
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -84,11 +86,11 @@ class ActivityPubFollowServiceImplTest {
|
||||||
|
|
||||||
)
|
)
|
||||||
val activityPubUserService = mock<ActivityPubUserService> {
|
val activityPubUserService = mock<ActivityPubUserService> {
|
||||||
onBlocking { fetchPerson(anyString()) } doReturn person
|
onBlocking { fetchPerson(anyString(), any()) } doReturn person
|
||||||
}
|
}
|
||||||
val userService = mock<UserService> {
|
val userService = mock<IUserService> {
|
||||||
onBlocking { findByUrls(any()) } doReturn listOf(
|
onBlocking { findByUrls(any()) } doReturn listOf(
|
||||||
UserEntity(
|
User(
|
||||||
id = 1L,
|
id = 1L,
|
||||||
name = "test",
|
name = "test",
|
||||||
domain = "example.com",
|
domain = "example.com",
|
||||||
|
@ -96,9 +98,11 @@ class ActivityPubFollowServiceImplTest {
|
||||||
description = "This user is test user.",
|
description = "This user is test user.",
|
||||||
inbox = "https://example.com/inbox",
|
inbox = "https://example.com/inbox",
|
||||||
outbox = "https://example.com/outbox",
|
outbox = "https://example.com/outbox",
|
||||||
url = "https://example.com"
|
url = "https://example.com",
|
||||||
|
publicKey = "",
|
||||||
|
createdAt = Instant.now()
|
||||||
),
|
),
|
||||||
UserEntity(
|
User(
|
||||||
id = 2L,
|
id = 2L,
|
||||||
name = "follower",
|
name = "follower",
|
||||||
domain = "follower.example.com",
|
domain = "follower.example.com",
|
||||||
|
@ -106,7 +110,9 @@ class ActivityPubFollowServiceImplTest {
|
||||||
description = "This user is test follower user.",
|
description = "This user is test follower user.",
|
||||||
inbox = "https://follower.example.com/inbox",
|
inbox = "https://follower.example.com/inbox",
|
||||||
outbox = "https://follower.example.com/outbox",
|
outbox = "https://follower.example.com/outbox",
|
||||||
url = "https://follower.example.com"
|
url = "https://follower.example.com",
|
||||||
|
publicKey = "",
|
||||||
|
createdAt = Instant.now()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
onBlocking { addFollowers(any(), any()) } doReturn Unit
|
onBlocking { addFollowers(any(), any()) } doReturn Unit
|
||||||
|
|
|
@ -6,9 +6,9 @@ package dev.usbharu.hideout.service.activitypub
|
||||||
import dev.usbharu.hideout.config.Config
|
import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.config.ConfigData
|
import dev.usbharu.hideout.config.ConfigData
|
||||||
import dev.usbharu.hideout.domain.model.PostEntity
|
import dev.usbharu.hideout.domain.model.PostEntity
|
||||||
import dev.usbharu.hideout.domain.model.UserEntity
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import dev.usbharu.hideout.service.job.JobQueueParentService
|
import dev.usbharu.hideout.service.job.JobQueueParentService
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.engine.mock.*
|
import io.ktor.client.engine.mock.*
|
||||||
|
@ -20,13 +20,14 @@ import org.junit.jupiter.api.Test
|
||||||
import org.mockito.Mockito.eq
|
import org.mockito.Mockito.eq
|
||||||
import org.mockito.kotlin.*
|
import org.mockito.kotlin.*
|
||||||
import utils.JsonObjectMapper
|
import utils.JsonObjectMapper
|
||||||
|
import java.time.Instant
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class ActivityPubNoteServiceImplTest {
|
class ActivityPubNoteServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `createPost 新しい投稿`() = runTest {
|
fun `createPost 新しい投稿`() = runTest {
|
||||||
val followers = listOf<UserEntity>(
|
val followers = listOf<User>(
|
||||||
UserEntity(
|
User(
|
||||||
2L,
|
2L,
|
||||||
"follower",
|
"follower",
|
||||||
"follower.example.com",
|
"follower.example.com",
|
||||||
|
@ -34,9 +35,12 @@ class ActivityPubNoteServiceImplTest {
|
||||||
"test follower user",
|
"test follower user",
|
||||||
"https://follower.example.com/inbox",
|
"https://follower.example.com/inbox",
|
||||||
"https://follower.example.com/outbox",
|
"https://follower.example.com/outbox",
|
||||||
"https://follower.example.com"
|
"https://follower.example.com",
|
||||||
|
"",
|
||||||
|
publicKey = "",
|
||||||
|
createdAt = Instant.now()
|
||||||
),
|
),
|
||||||
UserEntity(
|
User(
|
||||||
3L,
|
3L,
|
||||||
"follower2",
|
"follower2",
|
||||||
"follower2.example.com",
|
"follower2.example.com",
|
||||||
|
@ -44,11 +48,14 @@ class ActivityPubNoteServiceImplTest {
|
||||||
"test follower2 user",
|
"test follower2 user",
|
||||||
"https://follower2.example.com/inbox",
|
"https://follower2.example.com/inbox",
|
||||||
"https://follower2.example.com/outbox",
|
"https://follower2.example.com/outbox",
|
||||||
"https:.//follower2.example.com"
|
"https://follower2.example.com",
|
||||||
|
"",
|
||||||
|
publicKey = "",
|
||||||
|
createdAt = Instant.now()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val userService = mock<UserService> {
|
val userService = mock<IUserService> {
|
||||||
onBlocking { findById(eq(1L)) } doReturn UserEntity(
|
onBlocking { findById(eq(1L)) } doReturn User(
|
||||||
1L,
|
1L,
|
||||||
"test",
|
"test",
|
||||||
"example.com",
|
"example.com",
|
||||||
|
@ -56,7 +63,10 @@ class ActivityPubNoteServiceImplTest {
|
||||||
"test user",
|
"test user",
|
||||||
"https://example.com/inbox",
|
"https://example.com/inbox",
|
||||||
"https://example.com/outbox",
|
"https://example.com/outbox",
|
||||||
"https:.//example.com"
|
"https:.//example.com",
|
||||||
|
"",
|
||||||
|
publicKey = "",
|
||||||
|
createdAt = Instant.now()
|
||||||
)
|
)
|
||||||
onBlocking { findFollowersById(eq(1L)) } doReturn followers
|
onBlocking { findFollowersById(eq(1L)) } doReturn followers
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
||||||
|
package dev.usbharu.hideout.service.impl
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.config.Config
|
||||||
|
import dev.usbharu.hideout.config.ConfigData
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto
|
||||||
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
|
import dev.usbharu.hideout.service.IUserAuthService
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.ArgumentMatchers.anyString
|
||||||
|
import org.mockito.kotlin.*
|
||||||
|
import java.security.KeyPairGenerator
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
|
class UserServiceTest{
|
||||||
|
@Test
|
||||||
|
fun `createLocalUser ローカルユーザーを作成できる`() = runTest {
|
||||||
|
Config.configData = ConfigData(domain = "example.com", url = "https://example.com")
|
||||||
|
val userRepository = mock<IUserRepository> {
|
||||||
|
onBlocking { nextId() } doReturn 110001L
|
||||||
|
}
|
||||||
|
val generateKeyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair()
|
||||||
|
val userAuthService = mock<IUserAuthService> {
|
||||||
|
onBlocking { hash(anyString()) } doReturn "hashedPassword"
|
||||||
|
onBlocking { generateKeyPair() } doReturn generateKeyPair
|
||||||
|
}
|
||||||
|
val userService = UserService(userRepository, userAuthService)
|
||||||
|
userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test"))
|
||||||
|
verify(userRepository, times(1)).save(any())
|
||||||
|
argumentCaptor<dev.usbharu.hideout.domain.model.hideout.entity.User> {
|
||||||
|
verify(userRepository, times(1)).save(capture())
|
||||||
|
assertEquals("test", firstValue.name)
|
||||||
|
assertEquals("testUser", firstValue.screenName)
|
||||||
|
assertEquals("XXXXXXXXXXXXX", firstValue.description)
|
||||||
|
assertEquals("hashedPassword", firstValue.password)
|
||||||
|
assertEquals(110001L, firstValue.id)
|
||||||
|
assertEquals("https://example.com/users/test", firstValue.url)
|
||||||
|
assertEquals("example.com", firstValue.domain)
|
||||||
|
assertEquals("https://example.com/users/test/inbox", firstValue.inbox)
|
||||||
|
assertEquals("https://example.com/users/test/outbox", firstValue.outbox)
|
||||||
|
assertEquals(generateKeyPair.public.toPem(),firstValue.publicKey)
|
||||||
|
assertEquals(generateKeyPair.private.toPem(),firstValue.privateKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `createRemoteUser リモートユーザーを作成できる`() = runTest {
|
||||||
|
|
||||||
|
Config.configData = ConfigData(domain = "example.com", url = "https://example.com")
|
||||||
|
|
||||||
|
|
||||||
|
val userRepository = mock<IUserRepository>{
|
||||||
|
onBlocking { nextId() } doReturn 113345L
|
||||||
|
}
|
||||||
|
val userService = UserService(userRepository,mock())
|
||||||
|
val user = RemoteUserCreateDto(
|
||||||
|
"test",
|
||||||
|
"example.com",
|
||||||
|
"testUser",
|
||||||
|
"test user",
|
||||||
|
"https://example.com/inbox",
|
||||||
|
"https://example.com/outbox",
|
||||||
|
"https://example.com",
|
||||||
|
"-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----"
|
||||||
|
)
|
||||||
|
userService.createRemoteUser(user)
|
||||||
|
verify(userRepository, times(1)).save(any())
|
||||||
|
argumentCaptor<dev.usbharu.hideout.domain.model.hideout.entity.User> {
|
||||||
|
verify(userRepository, times(1)).save(capture())
|
||||||
|
assertEquals("test", firstValue.name)
|
||||||
|
assertEquals("testUser", firstValue.screenName)
|
||||||
|
assertEquals("test user", firstValue.description)
|
||||||
|
assertNull(firstValue.password)
|
||||||
|
assertEquals(113345L, firstValue.id)
|
||||||
|
assertEquals("https://example.com", firstValue.url)
|
||||||
|
assertEquals("example.com", firstValue.domain)
|
||||||
|
assertEquals("https://example.com/inbox", firstValue.inbox)
|
||||||
|
assertEquals("https://example.com/outbox", firstValue.outbox)
|
||||||
|
assertEquals("-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----",firstValue.publicKey)
|
||||||
|
assertNull(firstValue.privateKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue