mirror of https://github.com/usbharu/Hideout.git
Merge pull request #294 from usbharu/feature/activitypub-security
Feature/activitypub security
This commit is contained in:
commit
a3942a0330
|
@ -44,7 +44,7 @@ Ja15+ZWbOA4vJA9pOh3x4XM=
|
||||||
-----END PRIVATE KEY-----
|
-----END PRIVATE KEY-----
|
||||||
', 1701398248417,
|
', 1701398248417,
|
||||||
'http://localhost/users/test-user#pubkey', 'http://localhost/users/test-user/following',
|
'http://localhost/users/test-user#pubkey', 'http://localhost/users/test-user/following',
|
||||||
'http://localhost/users/test-users/followers', null, false, 0, 0, 0, null);
|
'http://localhost/users/test-users/followers', 0, false, 0, 0, 0, null);
|
||||||
|
|
||||||
insert into user_details (actor_id, password, auto_accept_followee_follow_request)
|
insert into user_details (actor_id, password, auto_accept_followee_follow_request)
|
||||||
values ( 1730415786666758144
|
values ( 1730415786666758144
|
||||||
|
|
|
@ -7,11 +7,11 @@ VALUES (3733363, 'follow-test-user-1', 'example.com', 'follow-test-user-1-name',
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||||
'https://example.com/users/follow-test-user-1#pubkey', 'https://example.com/users/follow-test-user-1/following',
|
'https://example.com/users/follow-test-user-1#pubkey', 'https://example.com/users/follow-test-user-1/following',
|
||||||
'https://example.com/users/follow-test-user-1/followers', null, false, 0, 0, 0, null),
|
'https://example.com/users/follow-test-user-1/followers', 0, false, 0, 0, 0, null),
|
||||||
(37335363, 'follow-test-user-2', 'example.com', 'follow-test-user-2-name', '',
|
(37335363, 'follow-test-user-2', 'example.com', 'follow-test-user-2-name', '',
|
||||||
'https://example.com/users/follow-test-user-2/inbox',
|
'https://example.com/users/follow-test-user-2/inbox',
|
||||||
'https://example.com/users/follow-test-user-2/outbox', 'https://example.com/users/follow-test-user-2',
|
'https://example.com/users/follow-test-user-2/outbox', 'https://example.com/users/follow-test-user-2',
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||||
'https://example.com/users/follow-test-user-2#pubkey', 'https://example.com/users/follow-test-user-2/following',
|
'https://example.com/users/follow-test-user-2#pubkey', 'https://example.com/users/follow-test-user-2/following',
|
||||||
'https://example.com/users/follow-test-user-2/followers', null, false, 0, 0, 0, null);
|
'https://example.com/users/follow-test-user-2/followers', 0, false, 0, 0, 0, null);
|
||||||
|
|
|
@ -7,7 +7,7 @@ VALUES (8, 'test-user8', 'example.com', 'Im test-user8.', 'THis account is test-
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||||
'https://example.com/users/test-user8#pubkey', 'https://example.com/users/test-user8/following',
|
'https://example.com/users/test-user8#pubkey', 'https://example.com/users/test-user8/following',
|
||||||
'https://example.com/users/test-user8/followers', null, false, 0, 0, 0, null),
|
'https://example.com/users/test-user8/followers', 0, false, 0, 0, 0, null),
|
||||||
(9, 'test-user9', 'follower.example.com', 'Im test-user9.', 'THis account is test-user9.',
|
(9, 'test-user9', 'follower.example.com', 'Im test-user9.', 'THis account is test-user9.',
|
||||||
'https://follower.example.com/users/test-user9/inbox',
|
'https://follower.example.com/users/test-user9/inbox',
|
||||||
'https://follower.example.com/users/test-user9/outbox', 'https://follower.example.com/users/test-user9',
|
'https://follower.example.com/users/test-user9/outbox', 'https://follower.example.com/users/test-user9',
|
||||||
|
@ -15,7 +15,7 @@ VALUES (8, 'test-user8', 'example.com', 'Im test-user8.', 'THis account is test-
|
||||||
null, 12345678,
|
null, 12345678,
|
||||||
'https://follower.example.com/users/test-user9#pubkey',
|
'https://follower.example.com/users/test-user9#pubkey',
|
||||||
'https://follower.example.com/users/test-user9/following',
|
'https://follower.example.com/users/test-user9/following',
|
||||||
'https://follower.example.com/users/test-user9/followers', null, false, 0, 0, 0, null);
|
'https://follower.example.com/users/test-user9/followers', 0, false, 0, 0, 0, null);
|
||||||
|
|
||||||
insert into relationships (actor_id, target_actor_id, following, blocking, muting, follow_request,
|
insert into relationships (actor_id, target_actor_id, following, blocking, muting, follow_request,
|
||||||
ignore_follow_request)
|
ignore_follow_request)
|
||||||
|
|
|
@ -7,7 +7,7 @@ VALUES (4, 'test-user4', 'example.com', 'Im test user4.', 'THis account is test
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||||
'https://example.com/users/test-user4#pubkey', 'https://example.com/users/test-user4/following',
|
'https://example.com/users/test-user4#pubkey', 'https://example.com/users/test-user4/following',
|
||||||
'https://example.com/users/test-user4/followers', null, false, 0, 0, 0, null),
|
'https://example.com/users/test-user4/followers', 0, false, 0, 0, 0, null),
|
||||||
(5, 'test-user5', 'follower.example.com', 'Im test user5.', 'THis account is test user5.',
|
(5, 'test-user5', 'follower.example.com', 'Im test user5.', 'THis account is test user5.',
|
||||||
'https://follower.example.com/users/test-user5/inbox',
|
'https://follower.example.com/users/test-user5/inbox',
|
||||||
'https://follower.example.com/users/test-user5/outbox', 'https://follower.example.com/users/test-user5',
|
'https://follower.example.com/users/test-user5/outbox', 'https://follower.example.com/users/test-user5',
|
||||||
|
@ -15,7 +15,7 @@ VALUES (4, 'test-user4', 'example.com', 'Im test user4.', 'THis account is test
|
||||||
null, 12345678,
|
null, 12345678,
|
||||||
'https://follower.example.com/users/test-user5#pubkey',
|
'https://follower.example.com/users/test-user5#pubkey',
|
||||||
'https://follower.example.com/users/test-user5/following',
|
'https://follower.example.com/users/test-user5/following',
|
||||||
'https://follower.example.com/users/test-user5/followers', null, false, 0, 0, 0, null);
|
'https://follower.example.com/users/test-user5/followers', 0, false, 0, 0, 0, null);
|
||||||
|
|
||||||
insert into relationships (actor_id, target_actor_id, following, blocking, muting, follow_request,
|
insert into relationships (actor_id, target_actor_id, following, blocking, muting, follow_request,
|
||||||
ignore_follow_request)
|
ignore_follow_request)
|
||||||
|
|
|
@ -7,7 +7,7 @@ VALUES (6, 'test-user6', 'example.com', 'Im test-user6.', 'THis account is test-
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||||
'https://example.com/users/test-user6#pubkey', 'https://example.com/users/test-user6/following',
|
'https://example.com/users/test-user6#pubkey', 'https://example.com/users/test-user6/following',
|
||||||
'https://example.com/users/test-user6/followers', null, false, 0, 0, 0, null),
|
'https://example.com/users/test-user6/followers', 0, false, 0, 0, 0, null),
|
||||||
(7, 'test-user7', 'follower.example.com', 'Im test-user7.', 'THis account is test-user7.',
|
(7, 'test-user7', 'follower.example.com', 'Im test-user7.', 'THis account is test-user7.',
|
||||||
'https://follower.example.com/users/test-user7/inbox',
|
'https://follower.example.com/users/test-user7/inbox',
|
||||||
'https://follower.example.com/users/test-user7/outbox', 'https://follower.example.com/users/test-user7',
|
'https://follower.example.com/users/test-user7/outbox', 'https://follower.example.com/users/test-user7',
|
||||||
|
@ -15,7 +15,7 @@ VALUES (6, 'test-user6', 'example.com', 'Im test-user6.', 'THis account is test-
|
||||||
null, 12345678,
|
null, 12345678,
|
||||||
'https://follower.example.com/users/test-user7#pubkey',
|
'https://follower.example.com/users/test-user7#pubkey',
|
||||||
'https://follower.example.com/users/test-user7/following',
|
'https://follower.example.com/users/test-user7/following',
|
||||||
'https://follower.example.com/users/test-user7/followers', null, false, 0, 0, 0, null);
|
'https://follower.example.com/users/test-user7/followers', 0, false, 0, 0, 0, null);
|
||||||
|
|
||||||
insert into relationships (actor_id, target_actor_id, following, blocking, muting, follow_request,
|
insert into relationships (actor_id, target_actor_id, following, blocking, muting, follow_request,
|
||||||
ignore_follow_request)
|
ignore_follow_request)
|
||||||
|
|
|
@ -7,7 +7,7 @@ VALUES (11, 'test-user11', 'example.com', 'Im test-user11.', 'THis account is te
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||||
'https://example.com/users/test-user11#pubkey', 'https://example.com/users/test-user11/following',
|
'https://example.com/users/test-user11#pubkey', 'https://example.com/users/test-user11/following',
|
||||||
'https://example.com/users/test-user11/followers', null, false, 0, 0, 0, null);
|
'https://example.com/users/test-user11/followers', 0, false, 0, 0, 0, null);
|
||||||
|
|
||||||
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
||||||
ap_id,
|
ap_id,
|
||||||
|
|
|
@ -7,7 +7,7 @@ VALUES (10, 'test-user10', 'example.com', 'Im test-user10.', 'THis account is te
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||||
'https://example.com/users/test-user10#pubkey', 'https://example.com/users/test-user10/following',
|
'https://example.com/users/test-user10#pubkey', 'https://example.com/users/test-user10/following',
|
||||||
'https://example.com/users/test-user10/followers', null, false, 0, 0, 0, null);
|
'https://example.com/users/test-user10/followers', 0, false, 0, 0, 0, null);
|
||||||
|
|
||||||
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
||||||
ap_id,
|
ap_id,
|
||||||
|
|
|
@ -7,7 +7,7 @@ VALUES (3, 'test-user3', 'example.com', 'Im test user3.', 'THis account is test
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||||
'https://example.com/users/test-user3#pubkey', 'https://example.com/users/test-user3/following',
|
'https://example.com/users/test-user3#pubkey', 'https://example.com/users/test-user3/following',
|
||||||
'https://example.com/users/test-user3/followers', null, false, 0, 0, 0, null);
|
'https://example.com/users/test-user3/followers', 0, false, 0, 0, 0, null);
|
||||||
|
|
||||||
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
||||||
ap_id,
|
ap_id,
|
||||||
|
|
|
@ -7,7 +7,7 @@ VALUES (1, 'test-user', 'example.com', 'Im test user.', 'THis account is test us
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||||
'https://example.com/users/test-user#pubkey', 'https://example.com/users/test-user/following',
|
'https://example.com/users/test-user#pubkey', 'https://example.com/users/test-user/following',
|
||||||
'https://example.com/users/test-users/followers', null, false, 0, 0, 0, null);
|
'https://example.com/users/test-users/followers', 0, false, 0, 0, 0, null);
|
||||||
|
|
||||||
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
||||||
ap_id,
|
ap_id,
|
||||||
|
|
|
@ -7,7 +7,7 @@ VALUES (2, 'test-user2', 'example.com', 'Im test user2.', 'THis account is test
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||||
'https://example.com/users/test-user2#pubkey', 'https://example.com/users/test-user2/following',
|
'https://example.com/users/test-user2#pubkey', 'https://example.com/users/test-user2/following',
|
||||||
'https://example.com/users/test-user2/followers', null, false, 0, 0, 0, null);
|
'https://example.com/users/test-user2/followers', 0, false, 0, 0, 0, null);
|
||||||
|
|
||||||
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive,
|
||||||
ap_id,
|
ap_id,
|
||||||
|
|
|
@ -7,4 +7,4 @@ VALUES (1, 'test-user', 'example.com', 'Im test user.', 'THis account is test us
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||||
'https://example.com/users/test-user#pubkey', 'https://example.com/users/test-user/following',
|
'https://example.com/users/test-user#pubkey', 'https://example.com/users/test-user/following',
|
||||||
'https://example.com/users/test-users/followers', null, false, 0, 0, 0, null);
|
'https://example.com/users/test-users/followers', 0, false, 0, 0, 0, null);
|
||||||
|
|
|
@ -7,4 +7,4 @@ VALUES (2, 'test-user2', 'example.com', 'Im test user.', 'THis account is test u
|
||||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||||
'https://example.com/users/test-user2#pubkey', 'https://example.com/users/test-user2/following',
|
'https://example.com/users/test-user2#pubkey', 'https://example.com/users/test-user2/following',
|
||||||
'https://example.com/users/test-user2s/followers', null, false, 0, 0, 0, null);
|
'https://example.com/users/test-user2s/followers', 0, false, 0, 0, 0, null);
|
||||||
|
|
|
@ -18,37 +18,55 @@ package dev.usbharu.hideout.core.domain.model.actor
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||||
import dev.usbharu.hideout.application.config.CharacterLimit
|
import dev.usbharu.hideout.application.config.CharacterLimit
|
||||||
|
import jakarta.validation.Validator
|
||||||
|
import jakarta.validation.constraints.*
|
||||||
|
import org.hibernate.validator.constraints.URL
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
data class Actor private constructor(
|
data class Actor private constructor(
|
||||||
|
@get:NotNull
|
||||||
|
@get:Positive
|
||||||
val id: Long,
|
val id: Long,
|
||||||
|
@get:Pattern(regexp = "^[a-zA-Z0-9_-]{1,300}\$")
|
||||||
|
@get:Size(min = 1)
|
||||||
val name: String,
|
val name: String,
|
||||||
val domain: String,
|
val domain: String,
|
||||||
val screenName: String,
|
val screenName: String,
|
||||||
val description: String,
|
val description: String,
|
||||||
|
@get:URL
|
||||||
val inbox: String,
|
val inbox: String,
|
||||||
|
@get:URL
|
||||||
val outbox: String,
|
val outbox: String,
|
||||||
|
@get:URL
|
||||||
val url: String,
|
val url: String,
|
||||||
|
@get:NotBlank
|
||||||
val publicKey: String,
|
val publicKey: String,
|
||||||
val privateKey: String? = null,
|
val privateKey: String? = null,
|
||||||
|
@get:PastOrPresent
|
||||||
val createdAt: Instant,
|
val createdAt: Instant,
|
||||||
|
@get:NotBlank
|
||||||
val keyId: String,
|
val keyId: String,
|
||||||
val followers: String? = null,
|
val followers: String? = null,
|
||||||
val following: String? = null,
|
val following: String? = null,
|
||||||
val instance: Long? = null,
|
@get:PositiveOrZero
|
||||||
|
val instance: Long,
|
||||||
val locked: Boolean,
|
val locked: Boolean,
|
||||||
val followersCount: Int = 0,
|
val followersCount: Int = 0,
|
||||||
val followingCount: Int = 0,
|
val followingCount: Int = 0,
|
||||||
val postsCount: Int = 0,
|
val postsCount: Int = 0,
|
||||||
val lastPostDate: Instant? = null,
|
val lastPostDate: Instant? = null,
|
||||||
val emojis: List<Long> = emptyList()
|
val emojis: List<Long> = emptyList(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class UserBuilder(private val characterLimit: CharacterLimit, private val applicationConfig: ApplicationConfig) {
|
class UserBuilder(
|
||||||
|
private val characterLimit: CharacterLimit,
|
||||||
|
private val applicationConfig: ApplicationConfig,
|
||||||
|
private val validator: Validator,
|
||||||
|
) {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(UserBuilder::class.java)
|
private val logger = LoggerFactory.getLogger(UserBuilder::class.java)
|
||||||
|
|
||||||
|
@ -68,13 +86,13 @@ data class Actor private constructor(
|
||||||
keyId: String,
|
keyId: String,
|
||||||
following: String? = null,
|
following: String? = null,
|
||||||
followers: String? = null,
|
followers: String? = null,
|
||||||
instance: Long? = null,
|
instance: Long,
|
||||||
locked: Boolean,
|
locked: Boolean,
|
||||||
followersCount: Int = 0,
|
followersCount: Int = 0,
|
||||||
followingCount: Int = 0,
|
followingCount: Int = 0,
|
||||||
postsCount: Int = 0,
|
postsCount: Int = 0,
|
||||||
lastPostDate: Instant? = null,
|
lastPostDate: Instant? = null,
|
||||||
emojis: List<Long> = emptyList()
|
emojis: List<Long> = emptyList(),
|
||||||
): Actor {
|
): Actor {
|
||||||
if (id == 0L) {
|
if (id == 0L) {
|
||||||
return Actor(
|
return Actor(
|
||||||
|
@ -176,7 +194,7 @@ data class Actor private constructor(
|
||||||
"keyId must contain non-blank characters."
|
"keyId must contain non-blank characters."
|
||||||
}
|
}
|
||||||
|
|
||||||
return Actor(
|
val actor = Actor(
|
||||||
id = id,
|
id = id,
|
||||||
name = limitedName,
|
name = limitedName,
|
||||||
domain = domain,
|
domain = domain,
|
||||||
|
@ -199,6 +217,13 @@ data class Actor private constructor(
|
||||||
lastPostDate = lastPostDate,
|
lastPostDate = lastPostDate,
|
||||||
emojis = emojis
|
emojis = emojis
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val validate = validator.validate(actor)
|
||||||
|
|
||||||
|
for (constraintViolation in validate) {
|
||||||
|
throw IllegalArgumentException("${constraintViolation.propertyPath} : ${constraintViolation.message}")
|
||||||
|
}
|
||||||
|
return actor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,27 +242,27 @@ data class Actor private constructor(
|
||||||
fun withLastPostAt(lastPostDate: Instant): Actor = this.copy(lastPostDate = lastPostDate)
|
fun withLastPostAt(lastPostDate: Instant): Actor = this.copy(lastPostDate = lastPostDate)
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Actor(" +
|
return "Actor(" +
|
||||||
"id=$id, " +
|
"id=$id, " +
|
||||||
"name='$name', " +
|
"name='$name', " +
|
||||||
"domain='$domain', " +
|
"domain='$domain', " +
|
||||||
"screenName='$screenName', " +
|
"screenName='$screenName', " +
|
||||||
"description='$description', " +
|
"description='$description', " +
|
||||||
"inbox='$inbox', " +
|
"inbox='$inbox', " +
|
||||||
"outbox='$outbox', " +
|
"outbox='$outbox', " +
|
||||||
"url='$url', " +
|
"url='$url', " +
|
||||||
"publicKey='$publicKey', " +
|
"publicKey='$publicKey', " +
|
||||||
"privateKey=$privateKey, " +
|
"privateKey=$privateKey, " +
|
||||||
"createdAt=$createdAt, " +
|
"createdAt=$createdAt, " +
|
||||||
"keyId='$keyId', " +
|
"keyId='$keyId', " +
|
||||||
"followers=$followers, " +
|
"followers=$followers, " +
|
||||||
"following=$following, " +
|
"following=$following, " +
|
||||||
"instance=$instance, " +
|
"instance=$instance, " +
|
||||||
"locked=$locked, " +
|
"locked=$locked, " +
|
||||||
"followersCount=$followersCount, " +
|
"followersCount=$followersCount, " +
|
||||||
"followingCount=$followingCount, " +
|
"followingCount=$followingCount, " +
|
||||||
"postsCount=$postsCount, " +
|
"postsCount=$postsCount, " +
|
||||||
"lastPostDate=$lastPostDate, " +
|
"lastPostDate=$lastPostDate, " +
|
||||||
"emojis=$emojis" +
|
"emojis=$emojis" +
|
||||||
")"
|
")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,31 +18,40 @@ package dev.usbharu.hideout.core.domain.model.post
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.config.CharacterLimit
|
import dev.usbharu.hideout.application.config.CharacterLimit
|
||||||
import dev.usbharu.hideout.core.service.post.PostContentFormatter
|
import dev.usbharu.hideout.core.service.post.PostContentFormatter
|
||||||
|
import jakarta.validation.Validator
|
||||||
|
import jakarta.validation.constraints.Positive
|
||||||
|
import org.hibernate.validator.constraints.URL
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
data class Post private constructor(
|
data class Post private constructor(
|
||||||
|
@get:Positive
|
||||||
val id: Long,
|
val id: Long,
|
||||||
|
@get:Positive
|
||||||
val actorId: Long,
|
val actorId: Long,
|
||||||
val overview: String? = null,
|
val overview: String? = null,
|
||||||
val content: String,
|
val content: String,
|
||||||
val text: String,
|
val text: String,
|
||||||
|
@get:Positive
|
||||||
val createdAt: Long,
|
val createdAt: Long,
|
||||||
val visibility: Visibility,
|
val visibility: Visibility,
|
||||||
|
@get:URL
|
||||||
val url: String,
|
val url: String,
|
||||||
val repostId: Long? = null,
|
val repostId: Long? = null,
|
||||||
val replyId: Long? = null,
|
val replyId: Long? = null,
|
||||||
val sensitive: Boolean = false,
|
val sensitive: Boolean = false,
|
||||||
|
@get:URL
|
||||||
val apId: String = url,
|
val apId: String = url,
|
||||||
val mediaIds: List<Long> = emptyList(),
|
val mediaIds: List<Long> = emptyList(),
|
||||||
val delted: Boolean = false,
|
val delted: Boolean = false,
|
||||||
val emojiIds: List<Long> = emptyList()
|
val emojiIds: List<Long> = emptyList(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class PostBuilder(
|
class PostBuilder(
|
||||||
private val characterLimit: CharacterLimit,
|
private val characterLimit: CharacterLimit,
|
||||||
private val postContentFormatter: PostContentFormatter
|
private val postContentFormatter: PostContentFormatter,
|
||||||
|
private val validator: Validator,
|
||||||
) {
|
) {
|
||||||
@Suppress("FunctionMinLength", "LongParameterList")
|
@Suppress("FunctionMinLength", "LongParameterList")
|
||||||
fun of(
|
fun of(
|
||||||
|
@ -86,7 +95,7 @@ data class Post private constructor(
|
||||||
require((repostId ?: 0) >= 0) { "repostId must be greater then or equal to 0." }
|
require((repostId ?: 0) >= 0) { "repostId must be greater then or equal to 0." }
|
||||||
require((replyId ?: 0) >= 0) { "replyId must be greater then or equal to 0." }
|
require((replyId ?: 0) >= 0) { "replyId must be greater then or equal to 0." }
|
||||||
|
|
||||||
return Post(
|
val post = Post(
|
||||||
id = id,
|
id = id,
|
||||||
actorId = actorId,
|
actorId = actorId,
|
||||||
overview = limitedOverview,
|
overview = limitedOverview,
|
||||||
|
@ -103,6 +112,14 @@ data class Post private constructor(
|
||||||
delted = false,
|
delted = false,
|
||||||
emojiIds = emojiIds
|
emojiIds = emojiIds
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val validate = validator.validate(post)
|
||||||
|
|
||||||
|
for (constraintViolation in validate) {
|
||||||
|
throw IllegalArgumentException("${constraintViolation.propertyPath} : ${constraintViolation.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
return post
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
|
@ -126,7 +143,7 @@ data class Post private constructor(
|
||||||
|
|
||||||
require(actorId >= 0) { "actorId must be greater than or equal to 0." }
|
require(actorId >= 0) { "actorId must be greater than or equal to 0." }
|
||||||
|
|
||||||
return Post(
|
val post = Post(
|
||||||
id = id,
|
id = id,
|
||||||
actorId = actorId,
|
actorId = actorId,
|
||||||
overview = null,
|
overview = null,
|
||||||
|
@ -143,6 +160,14 @@ data class Post private constructor(
|
||||||
delted = false,
|
delted = false,
|
||||||
emojiIds = emptyList()
|
emojiIds = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val validate = validator.validate(post)
|
||||||
|
|
||||||
|
for (constraintViolation in validate) {
|
||||||
|
throw IllegalArgumentException("${constraintViolation.propertyPath} : ${constraintViolation.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
return post
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
|
@ -193,7 +218,7 @@ data class Post private constructor(
|
||||||
|
|
||||||
require((replyId ?: 0) >= 0) { "replyId must be greater then or equal to 0." }
|
require((replyId ?: 0) >= 0) { "replyId must be greater then or equal to 0." }
|
||||||
|
|
||||||
return Post(
|
val post = Post(
|
||||||
id = id,
|
id = id,
|
||||||
actorId = actorId,
|
actorId = actorId,
|
||||||
overview = limitedOverview,
|
overview = limitedOverview,
|
||||||
|
@ -210,6 +235,14 @@ data class Post private constructor(
|
||||||
delted = false,
|
delted = false,
|
||||||
emojiIds = emojiIds
|
emojiIds = emojiIds
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val validate = validator.validate(post)
|
||||||
|
|
||||||
|
for (constraintViolation in validate) {
|
||||||
|
throw IllegalArgumentException("${constraintViolation.propertyPath} : ${constraintViolation.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
return post
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
|
|
|
@ -167,7 +167,7 @@ object Actors : Table("actors") {
|
||||||
val keyId = varchar("key_id", length = 1000)
|
val keyId = varchar("key_id", length = 1000)
|
||||||
val following = varchar("following", length = 1000).nullable()
|
val following = varchar("following", length = 1000).nullable()
|
||||||
val followers = varchar("followers", length = 1000).nullable()
|
val followers = varchar("followers", length = 1000).nullable()
|
||||||
val instance = long("instance").references(Instance.id).nullable()
|
val instance = long("instance").references(Instance.id)
|
||||||
val locked = bool("locked")
|
val locked = bool("locked")
|
||||||
val followingCount = integer("following_count")
|
val followingCount = integer("following_count")
|
||||||
val followersCount = integer("followers_count")
|
val followersCount = integer("followers_count")
|
||||||
|
|
|
@ -37,7 +37,7 @@ interface InstanceService {
|
||||||
class InstanceServiceImpl(
|
class InstanceServiceImpl(
|
||||||
private val instanceRepository: InstanceRepository,
|
private val instanceRepository: InstanceRepository,
|
||||||
private val resourceResolveService: ResourceResolveService,
|
private val resourceResolveService: ResourceResolveService,
|
||||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper
|
@Qualifier("activitypub") private val objectMapper: ObjectMapper,
|
||||||
) : InstanceService {
|
) : InstanceService {
|
||||||
override suspend fun fetchInstance(url: String, sharedInbox: String?): Instance {
|
override suspend fun fetchInstance(url: String, sharedInbox: String?): Instance {
|
||||||
val u = URL(url)
|
val u = URL(url)
|
||||||
|
@ -50,57 +50,53 @@ class InstanceServiceImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Instance not found. try fetch instance info. url: {}", resolveInstanceUrl)
|
logger.info("Instance not found. try fetch instance info. url: {}", resolveInstanceUrl)
|
||||||
|
@Suppress("TooGenericExceptionCaught")
|
||||||
|
try {
|
||||||
|
val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText()
|
||||||
|
val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java)
|
||||||
|
val nodeinfoPathMap = nodeinfo.links.associate { it.rel to it.href }
|
||||||
|
|
||||||
val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText()
|
for ((key, value) in nodeinfoPathMap) {
|
||||||
val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java)
|
when (key) {
|
||||||
val nodeinfoPathMap = nodeinfo.links.associate { it.rel to it.href }
|
"http://nodeinfo.diaspora.software/ns/schema/2.0",
|
||||||
|
"http://nodeinfo.diaspora.software/ns/schema/2.1",
|
||||||
|
-> {
|
||||||
|
val nodeinfo20 = objectMapper.readValue(
|
||||||
|
resourceResolveService.resolve(value!!).bodyAsText(),
|
||||||
|
Nodeinfo2_0::class.java
|
||||||
|
)
|
||||||
|
|
||||||
for ((key, value) in nodeinfoPathMap) {
|
val instanceCreateDto = InstanceCreateDto(
|
||||||
when (key) {
|
name = nodeinfo20.metadata?.nodeName,
|
||||||
"http://nodeinfo.diaspora.software/ns/schema/2.0" -> {
|
description = nodeinfo20.metadata?.nodeDescription,
|
||||||
val nodeinfo20 = objectMapper.readValue(
|
url = resolveInstanceUrl,
|
||||||
resourceResolveService.resolve(value!!).bodyAsText(),
|
iconUrl = "$resolveInstanceUrl/favicon.ico",
|
||||||
Nodeinfo2_0::class.java
|
sharedInbox = sharedInbox,
|
||||||
)
|
software = nodeinfo20.software?.name,
|
||||||
|
version = nodeinfo20.software?.version
|
||||||
|
)
|
||||||
|
return createNewInstance(instanceCreateDto)
|
||||||
|
}
|
||||||
|
|
||||||
val instanceCreateDto = InstanceCreateDto(
|
else -> {
|
||||||
name = nodeinfo20.metadata?.nodeName,
|
throw IllegalStateException("Unknown nodeinfo versions: $key url: $value")
|
||||||
description = nodeinfo20.metadata?.nodeDescription,
|
}
|
||||||
url = resolveInstanceUrl,
|
|
||||||
iconUrl = "$resolveInstanceUrl/favicon.ico",
|
|
||||||
sharedInbox = sharedInbox,
|
|
||||||
software = nodeinfo20.software?.name,
|
|
||||||
version = nodeinfo20.software?.version
|
|
||||||
)
|
|
||||||
return createNewInstance(instanceCreateDto)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 多分2.0と2.1で互換性有るのでそのまま使うけどなおす
|
|
||||||
"http://nodeinfo.diaspora.software/ns/schema/2.1" -> {
|
|
||||||
val nodeinfo20 = objectMapper.readValue(
|
|
||||||
resourceResolveService.resolve(value!!).bodyAsText(),
|
|
||||||
Nodeinfo2_0::class.java
|
|
||||||
)
|
|
||||||
|
|
||||||
val instanceCreateDto = InstanceCreateDto(
|
|
||||||
name = nodeinfo20.metadata?.nodeName,
|
|
||||||
description = nodeinfo20.metadata?.nodeDescription,
|
|
||||||
url = resolveInstanceUrl,
|
|
||||||
iconUrl = "$resolveInstanceUrl/favicon.ico",
|
|
||||||
sharedInbox = sharedInbox,
|
|
||||||
software = nodeinfo20.software?.name,
|
|
||||||
version = nodeinfo20.software?.version
|
|
||||||
)
|
|
||||||
return createNewInstance(instanceCreateDto)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
throw IllegalStateException("Unknown nodeinfo versions: $key url: $value")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.warn("FAILED Fetch Instance", e)
|
||||||
}
|
}
|
||||||
|
return createNewInstance(
|
||||||
throw IllegalStateException("Nodeinfo aren't found.")
|
InstanceCreateDto(
|
||||||
|
name = null,
|
||||||
|
description = null,
|
||||||
|
url = resolveInstanceUrl,
|
||||||
|
iconUrl = "$resolveInstanceUrl/favicon.ico",
|
||||||
|
sharedInbox = null,
|
||||||
|
software = null,
|
||||||
|
version = null
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance {
|
override suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance {
|
||||||
|
|
|
@ -78,7 +78,8 @@ class UserServiceImpl(
|
||||||
following = "$userUrl/following",
|
following = "$userUrl/following",
|
||||||
followers = "$userUrl/followers",
|
followers = "$userUrl/followers",
|
||||||
keyId = "$userUrl#pubkey",
|
keyId = "$userUrl#pubkey",
|
||||||
locked = false
|
locked = false,
|
||||||
|
instance = 0
|
||||||
)
|
)
|
||||||
val save = actorRepository.save(userEntity)
|
val save = actorRepository.save(userEntity)
|
||||||
userDetailRepository.save(UserDetail(nextId, hashedPassword, true))
|
userDetailRepository.save(UserDetail(nextId, hashedPassword, true))
|
||||||
|
@ -95,13 +96,7 @@ class UserServiceImpl(
|
||||||
throw IllegalStateException("Cannot create Deleted actor.")
|
throw IllegalStateException("Cannot create Deleted actor.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
val instance = instanceService.fetchInstance(user.url, user.sharedInbox)
|
||||||
val instance = try {
|
|
||||||
instanceService.fetchInstance(user.url, user.sharedInbox)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.warn("FAILED to fetch instance. url: {}", user.url, e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
val nextId = actorRepository.nextId()
|
val nextId = actorRepository.nextId()
|
||||||
val userEntity = actorBuilder.of(
|
val userEntity = actorBuilder.of(
|
||||||
|
@ -118,7 +113,7 @@ class UserServiceImpl(
|
||||||
followers = user.followers,
|
followers = user.followers,
|
||||||
following = user.following,
|
following = user.following,
|
||||||
keyId = user.keyId,
|
keyId = user.keyId,
|
||||||
instance = instance?.id,
|
instance = instance.id,
|
||||||
locked = user.locked ?: false
|
locked = user.locked ?: false
|
||||||
)
|
)
|
||||||
return try {
|
return try {
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.springframework.security.web.SecurityFilterChain
|
||||||
class MastodonApiSecurityConfig {
|
class MastodonApiSecurityConfig {
|
||||||
@Bean
|
@Bean
|
||||||
@Order(4)
|
@Order(4)
|
||||||
|
@Suppress("LongMethod")
|
||||||
fun mastodonApiSecurityFilterChain(
|
fun mastodonApiSecurityFilterChain(
|
||||||
http: HttpSecurity,
|
http: HttpSecurity,
|
||||||
rf: RoleHierarchyAuthorizationManagerFactory,
|
rf: RoleHierarchyAuthorizationManagerFactory,
|
||||||
|
|
|
@ -45,7 +45,7 @@ create table if not exists actors
|
||||||
key_id varchar(1000) not null,
|
key_id varchar(1000) not null,
|
||||||
"following" varchar(1000) null,
|
"following" varchar(1000) null,
|
||||||
followers varchar(1000) null,
|
followers varchar(1000) null,
|
||||||
"instance" bigint null,
|
"instance" bigint not null,
|
||||||
locked boolean not null,
|
locked boolean not null,
|
||||||
following_count int not null,
|
following_count int not null,
|
||||||
followers_count int not null,
|
followers_count int not null,
|
||||||
|
@ -240,10 +240,14 @@ create table if not exists relationships
|
||||||
unique (actor_id, target_actor_id)
|
unique (actor_id, target_actor_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
insert into instance (id, name, description, url, icon_url, shared_inbox, software, version, is_blocked, is_muted,
|
||||||
|
moderation_note, created_at)
|
||||||
|
values (0, 'system', '', '', '', null, '', '', false, false, '', current_timestamp);
|
||||||
|
|
||||||
insert into actors (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key, created_at,
|
insert into actors (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key, created_at,
|
||||||
key_id, following, followers, instance, locked, following_count, followers_count, posts_count,
|
key_id, following, followers, instance, locked, following_count, followers_count, posts_count,
|
||||||
last_post_at)
|
last_post_at)
|
||||||
values (0, 'ghost', '', '', '', '', '', '', '', null, 0, '', '', '', null, true, 0, 0, 0, null);
|
values (0, 'ghost', '', '', '', '', '', '', '', null, 0, '', '', '', 0, true, 0, 0, 0, null);
|
||||||
|
|
||||||
create table if not exists deleted_actors
|
create table if not exists deleted_actors
|
||||||
(
|
(
|
||||||
|
|
|
@ -45,6 +45,7 @@ import io.ktor.http.*
|
||||||
import io.ktor.http.content.*
|
import io.ktor.http.content.*
|
||||||
import io.ktor.util.*
|
import io.ktor.util.*
|
||||||
import io.ktor.util.date.*
|
import io.ktor.util.date.*
|
||||||
|
import jakarta.validation.Validation
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
@ -60,7 +61,10 @@ import java.time.Instant
|
||||||
|
|
||||||
class APNoteServiceImplTest {
|
class APNoteServiceImplTest {
|
||||||
|
|
||||||
val postBuilder = Post.PostBuilder(CharacterLimit(), DefaultPostContentFormatter(HtmlSanitizeConfig().policy()))
|
val postBuilder = Post.PostBuilder(
|
||||||
|
CharacterLimit(), DefaultPostContentFormatter(HtmlSanitizeConfig().policy()),
|
||||||
|
Validation.buildDefaultValidatorFactory().validator
|
||||||
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `fetchNote(String,String) ノートが既に存在する場合はDBから取得したものを返す`() = runTest {
|
fun `fetchNote(String,String) ノートが既に存在する場合はDBから取得したものを返す`() = runTest {
|
||||||
|
@ -89,10 +93,7 @@ class APNoteServiceImplTest {
|
||||||
apUserService = mock(),
|
apUserService = mock(),
|
||||||
postService = mock(),
|
postService = mock(),
|
||||||
apResourceResolveService = mock(),
|
apResourceResolveService = mock(),
|
||||||
postBuilder = Post.PostBuilder(
|
postBuilder = postBuilder,
|
||||||
CharacterLimit(),
|
|
||||||
DefaultPostContentFormatter(HtmlSanitizeConfig().policy())
|
|
||||||
),
|
|
||||||
noteQueryService = noteQueryService,
|
noteQueryService = noteQueryService,
|
||||||
mock(),
|
mock(),
|
||||||
mock(),
|
mock(),
|
||||||
|
@ -163,10 +164,7 @@ class APNoteServiceImplTest {
|
||||||
apUserService = apUserService,
|
apUserService = apUserService,
|
||||||
postService = mock(),
|
postService = mock(),
|
||||||
apResourceResolveService = apResourceResolveService,
|
apResourceResolveService = apResourceResolveService,
|
||||||
postBuilder = Post.PostBuilder(
|
postBuilder = postBuilder,
|
||||||
CharacterLimit(),
|
|
||||||
DefaultPostContentFormatter(HtmlSanitizeConfig().policy())
|
|
||||||
),
|
|
||||||
noteQueryService = noteQueryService,
|
noteQueryService = noteQueryService,
|
||||||
mock(),
|
mock(),
|
||||||
mock { },
|
mock { },
|
||||||
|
@ -216,14 +214,11 @@ class APNoteServiceImplTest {
|
||||||
apUserService = mock(),
|
apUserService = mock(),
|
||||||
postService = mock(),
|
postService = mock(),
|
||||||
apResourceResolveService = apResourceResolveService,
|
apResourceResolveService = apResourceResolveService,
|
||||||
postBuilder = Post.PostBuilder(
|
postBuilder = postBuilder,
|
||||||
CharacterLimit(),
|
|
||||||
DefaultPostContentFormatter(HtmlSanitizeConfig().policy())
|
|
||||||
),
|
|
||||||
noteQueryService = noteQueryService,
|
noteQueryService = noteQueryService,
|
||||||
mock(),
|
mock(),
|
||||||
mock(),
|
mock(),
|
||||||
mock { }
|
mock { }
|
||||||
)
|
)
|
||||||
|
|
||||||
assertThrows<FailedToGetActivityPubResourceException> { apNoteServiceImpl.fetchNote(url) }
|
assertThrows<FailedToGetActivityPubResourceException> { apNoteServiceImpl.fetchNote(url) }
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package dev.usbharu.hideout.core.domain.model.actor
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import utils.UserBuilder
|
||||||
|
|
||||||
|
class ActorTest {
|
||||||
|
@Test
|
||||||
|
fun validator() {
|
||||||
|
org.junit.jupiter.api.assertThrows<IllegalArgumentException> {
|
||||||
|
UserBuilder.localUserOf(name = "うんこ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ import dev.usbharu.hideout.core.domain.model.post.Post
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostRepository
|
import dev.usbharu.hideout.core.domain.model.post.PostRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository
|
import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository
|
||||||
import dev.usbharu.hideout.core.service.timeline.TimelineService
|
import dev.usbharu.hideout.core.service.timeline.TimelineService
|
||||||
|
import jakarta.validation.Validation
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
@ -56,7 +57,7 @@ class PostServiceImplTest {
|
||||||
private var postBuilder: Post.PostBuilder = Post.PostBuilder(
|
private var postBuilder: Post.PostBuilder = Post.PostBuilder(
|
||||||
CharacterLimit(), DefaultPostContentFormatter(
|
CharacterLimit(), DefaultPostContentFormatter(
|
||||||
HtmlSanitizeConfig().policy()
|
HtmlSanitizeConfig().policy()
|
||||||
)
|
), Validation.buildDefaultValidatorFactory().validator
|
||||||
)
|
)
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
|
|
|
@ -20,11 +20,11 @@ package dev.usbharu.hideout.core.service.user
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||||
import dev.usbharu.hideout.application.config.CharacterLimit
|
import dev.usbharu.hideout.application.config.CharacterLimit
|
||||||
import dev.usbharu.hideout.application.config.HtmlSanitizeConfig
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
import dev.usbharu.hideout.core.domain.model.instance.Instance
|
||||||
import dev.usbharu.hideout.core.service.post.DefaultPostContentFormatter
|
import dev.usbharu.hideout.core.service.instance.InstanceService
|
||||||
|
import jakarta.validation.Validation
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
@ -33,12 +33,17 @@ import org.mockito.kotlin.*
|
||||||
import utils.TestApplicationConfig.testApplicationConfig
|
import utils.TestApplicationConfig.testApplicationConfig
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.security.KeyPairGenerator
|
import java.security.KeyPairGenerator
|
||||||
|
import java.time.Instant
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
class ActorServiceTest {
|
class ActorServiceTest {
|
||||||
val actorBuilder = Actor.UserBuilder(CharacterLimit(), ApplicationConfig(URL("https://example.com")))
|
val actorBuilder = Actor.UserBuilder(
|
||||||
val postBuilder = Post.PostBuilder(CharacterLimit(), DefaultPostContentFormatter(HtmlSanitizeConfig().policy()))
|
CharacterLimit(),
|
||||||
|
ApplicationConfig(URL("https://example.com")),
|
||||||
|
Validation.buildDefaultValidatorFactory().validator
|
||||||
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `createLocalUser ローカルユーザーを作成できる`() = runTest {
|
fun `createLocalUser ローカルユーザーを作成できる`() = runTest {
|
||||||
|
|
||||||
|
@ -89,13 +94,34 @@ class ActorServiceTest {
|
||||||
onBlocking { nextId() } doReturn 113345L
|
onBlocking { nextId() } doReturn 113345L
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val instanceService = mock<InstanceService> {
|
||||||
|
onBlocking {
|
||||||
|
fetchInstance(
|
||||||
|
eq("https://remote.example.com"),
|
||||||
|
isNull()
|
||||||
|
)
|
||||||
|
} doReturn Instance(
|
||||||
|
12345L,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"https://remote.example.com",
|
||||||
|
"https://remote.example.com/favicon.ico",
|
||||||
|
null,
|
||||||
|
"unknown",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
"",
|
||||||
|
Instant.now()
|
||||||
|
)
|
||||||
|
}
|
||||||
val userService =
|
val userService =
|
||||||
UserServiceImpl(
|
UserServiceImpl(
|
||||||
actorRepository = actorRepository,
|
actorRepository = actorRepository,
|
||||||
userAuthService = mock(),
|
userAuthService = mock(),
|
||||||
actorBuilder = actorBuilder,
|
actorBuilder = actorBuilder,
|
||||||
applicationConfig = testApplicationConfig,
|
applicationConfig = testApplicationConfig,
|
||||||
instanceService = mock(),
|
instanceService = instanceService,
|
||||||
userDetailRepository = mock(),
|
userDetailRepository = mock(),
|
||||||
deletedActorRepository = mock(),
|
deletedActorRepository = mock(),
|
||||||
reactionRepository = mock(),
|
reactionRepository = mock(),
|
||||||
|
|
|
@ -22,13 +22,18 @@ import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateServ
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||||
import dev.usbharu.hideout.core.service.post.DefaultPostContentFormatter
|
import dev.usbharu.hideout.core.service.post.DefaultPostContentFormatter
|
||||||
|
import jakarta.validation.Validation
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
object PostBuilder {
|
object PostBuilder {
|
||||||
|
|
||||||
private val postBuilder =
|
private val postBuilder =
|
||||||
Post.PostBuilder(CharacterLimit(), DefaultPostContentFormatter(HtmlSanitizeConfig().policy()))
|
Post.PostBuilder(
|
||||||
|
CharacterLimit(),
|
||||||
|
DefaultPostContentFormatter(HtmlSanitizeConfig().policy()),
|
||||||
|
Validation.buildDefaultValidatorFactory().validator
|
||||||
|
)
|
||||||
|
|
||||||
private val idGenerator = TwitterSnowflakeIdGenerateService
|
private val idGenerator = TwitterSnowflakeIdGenerateService
|
||||||
|
|
||||||
|
@ -39,7 +44,7 @@ object PostBuilder {
|
||||||
text: String = "Hello World",
|
text: String = "Hello World",
|
||||||
createdAt: Long = Instant.now().toEpochMilli(),
|
createdAt: Long = Instant.now().toEpochMilli(),
|
||||||
visibility: Visibility = Visibility.PUBLIC,
|
visibility: Visibility = Visibility.PUBLIC,
|
||||||
url: String = "https://example.com/users/$userId/posts/$id"
|
url: String = "https://example.com/users/$userId/posts/$id",
|
||||||
): Post {
|
): Post {
|
||||||
return postBuilder.of(
|
return postBuilder.of(
|
||||||
id = id,
|
id = id,
|
||||||
|
|
|
@ -20,12 +20,16 @@ import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||||
import dev.usbharu.hideout.application.config.CharacterLimit
|
import dev.usbharu.hideout.application.config.CharacterLimit
|
||||||
import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService
|
import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||||
|
import jakarta.validation.Validation
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
object UserBuilder {
|
object UserBuilder {
|
||||||
private val actorBuilder = Actor.UserBuilder(CharacterLimit(), ApplicationConfig(URL("https://example.com")))
|
private val actorBuilder = Actor.UserBuilder(
|
||||||
|
CharacterLimit(), ApplicationConfig(URL("https://example.com")),
|
||||||
|
Validation.buildDefaultValidatorFactory().validator
|
||||||
|
)
|
||||||
|
|
||||||
private val idGenerator = TwitterSnowflakeIdGenerateService
|
private val idGenerator = TwitterSnowflakeIdGenerateService
|
||||||
|
|
||||||
|
@ -43,7 +47,7 @@ object UserBuilder {
|
||||||
createdAt: Instant = Instant.now(),
|
createdAt: Instant = Instant.now(),
|
||||||
keyId: String = "https://$domain/users/$id#pubkey",
|
keyId: String = "https://$domain/users/$id#pubkey",
|
||||||
followers: String = "https://$domain/users/$id/followers",
|
followers: String = "https://$domain/users/$id/followers",
|
||||||
following: String = "https://$domain/users/$id/following"
|
following: String = "https://$domain/users/$id/following",
|
||||||
): Actor {
|
): Actor {
|
||||||
return actorBuilder.of(
|
return actorBuilder.of(
|
||||||
id = id,
|
id = id,
|
||||||
|
@ -60,7 +64,8 @@ object UserBuilder {
|
||||||
keyId = keyId,
|
keyId = keyId,
|
||||||
followers = followers,
|
followers = followers,
|
||||||
following = following,
|
following = following,
|
||||||
locked = false
|
locked = false,
|
||||||
|
instance = 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +82,8 @@ object UserBuilder {
|
||||||
createdAt: Instant = Instant.now(),
|
createdAt: Instant = Instant.now(),
|
||||||
keyId: String = "https://$domain/$id#pubkey",
|
keyId: String = "https://$domain/$id#pubkey",
|
||||||
followers: String = "https://$domain/$id/followers",
|
followers: String = "https://$domain/$id/followers",
|
||||||
following: String = "https://$domain/$id/following"
|
following: String = "https://$domain/$id/following",
|
||||||
|
instanceId: Long = generateId(),
|
||||||
): Actor {
|
): Actor {
|
||||||
return actorBuilder.of(
|
return actorBuilder.of(
|
||||||
id = id,
|
id = id,
|
||||||
|
@ -94,7 +100,8 @@ object UserBuilder {
|
||||||
keyId = keyId,
|
keyId = keyId,
|
||||||
followers = followers,
|
followers = followers,
|
||||||
following = following,
|
following = following,
|
||||||
locked = false
|
locked = false,
|
||||||
|
instance = instanceId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue