From 5e7b538d1d815811fa47a9ef969477e18b33b93a Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 4 Jun 2024 23:46:09 +0900 Subject: [PATCH] =?UTF-8?q?=E5=AE=9F=E9=9A=9B=E3=81=AB=E8=B5=B7=E5=8B=95?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hideout-core/build.gradle.kts | 2 + .../hideout/core/config/ApplicationConfig.kt | 1 + .../hideout/core/config/SecurityConfig.kt | 30 +++++++++++++ .../domain/model/actor/ActorPrivateKey.kt | 19 +++++++- .../core/domain/model/actor/ActorPublicKey.kt | 19 +++++++- .../domain/model/actor/ActorRepository.kt | 1 + .../local/LocalActorDomainServiceImpl.kt | 41 ++++++++++++++++++ ...calActorMigrationCheckDomainServiceImpl.kt | 43 +++++++++++++++++++ .../ExposedActorRepository.kt | 15 ++++++- .../ExposedPostRepository.kt | 25 ++++++++--- .../SpringSecurityPasswordEncoder.kt | 28 ++++++++++++ .../src/main/resources/application.yml | 2 +- .../resources/db/migration/V1__Init_DB.sql | 21 ++++----- libs.versions.toml | 1 + 14 files changed, 227 insertions(+), 21 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt diff --git a/hideout-core/build.gradle.kts b/hideout-core/build.gradle.kts index 5920eb91..ca9a8cc9 100644 --- a/hideout-core/build.gradle.kts +++ b/hideout-core/build.gradle.kts @@ -165,6 +165,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-log4j2") implementation("org.springframework.boot:spring-boot-starter-validation") + implementation(libs.blurhash) implementation(libs.aws.s3) implementation(libs.jsoup) @@ -173,6 +174,7 @@ dependencies { implementation(libs.imageio.webp) implementation(libs.thumbnailator) implementation(libs.flyway.core) + runtimeOnly(libs.flyway.postgresql) implementation("dev.usbharu:owl-common-serialize-jackson:0.0.1") diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ApplicationConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ApplicationConfig.kt index b4b0e391..5cb5941c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ApplicationConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ApplicationConfig.kt @@ -23,4 +23,5 @@ import java.net.URL data class ApplicationConfig( val url: URL, val private: Boolean = true, + val keySize: Int = 2048, ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt new file mode 100644 index 00000000..f56f796b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder + +@Configuration +class SecurityConfig { + @Bean + fun passwordEncoder(): PasswordEncoder { + return BCryptPasswordEncoder() + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt index 2777c3e5..bb507885 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt @@ -16,5 +16,22 @@ package dev.usbharu.hideout.core.domain.model.actor +import java.security.PrivateKey +import java.util.* + @JvmInline -value class ActorPrivateKey(val privateKey: String) +value class ActorPrivateKey(val privateKey: String) { + companion object { + fun create(privateKey: PrivateKey): ActorPrivateKey { + return ActorPrivateKey( + "-----BEGIN PRIVATE KEY-----\n" + + Base64 + .getEncoder() + .encodeToString(privateKey.encoded) + .chunked(64) + .joinToString("\n") + + "\n-----END PRIVATE KEY-----" + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt index ac8cc06f..5dd982f1 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt @@ -16,5 +16,22 @@ package dev.usbharu.hideout.core.domain.model.actor +import java.security.PublicKey +import java.util.* + @JvmInline -value class ActorPublicKey(val publicKey: String) +value class ActorPublicKey(val publicKey: String) { + companion object { + fun create(publicKey: PublicKey): ActorPublicKey { + return ActorPublicKey( + "-----BEGIN PUBLIC KEY-----\n" + + Base64 + .getEncoder() + .encodeToString(publicKey.encoded) + .chunked(64) + .joinToString("\n") + + "\n-----END PUBLIC KEY-----" + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt index 0438660b..266e1d47 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt @@ -20,4 +20,5 @@ interface ActorRepository { suspend fun save(actor: Actor): Actor suspend fun delete(actor: Actor) suspend fun findById(id: ActorId): Actor? + suspend fun findByNameAndDomain(name: String, domain: String): Actor? } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt new file mode 100644 index 00000000..f00406e5 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.actor.local + +import dev.usbharu.hideout.core.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorPrivateKey +import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import org.springframework.stereotype.Service +import java.security.KeyPairGenerator + +@Service +class LocalActorDomainServiceImpl( + private val actorRepository: ActorRepository, + private val applicationConfig: ApplicationConfig, +) : LocalActorDomainService { + override suspend fun usernameAlreadyUse(name: String): Boolean = + actorRepository.findByNameAndDomain(name, applicationConfig.url.host) == null + + override suspend fun generateKeyPair(): Pair { + val keyPairGenerator = KeyPairGenerator.getInstance("RSA") + keyPairGenerator.initialize(applicationConfig.keySize) + val generateKeyPair = keyPairGenerator.generateKeyPair() + + return ActorPublicKey.create(generateKeyPair.public) to ActorPrivateKey.create(generateKeyPair.private) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt new file mode 100644 index 00000000..e2b2f621 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.actor.local + +import dev.usbharu.hideout.core.domain.model.actor.Actor +import org.springframework.stereotype.Service + +@Service +class LocalActorMigrationCheckDomainServiceImpl : LocalActorMigrationCheckDomainService { + override suspend fun canAccountMigration(from: Actor, to: Actor): AccountMigrationCheck { + if (to == from) { + return AccountMigrationCheck.SelfReferences() + } + + if (to.moveTo != null) { + return AccountMigrationCheck.AlreadyMoved("${to.name}@${to.domain} was move to ${to.moveTo}") + } + + if (from.moveTo != null) { + return AccountMigrationCheck.AlreadyMoved("${from.name}@${from.domain} was move to ${from.moveTo}") + } + + if (to.alsoKnownAs.contains(to.id).not()) { + return AccountMigrationCheck.AlsoKnownAsNotFound("${to.id} has ${to.alsoKnownAs}") + } + + return AccountMigrationCheck.CanAccountMigration() + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt index 9b16788f..14d17c3f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt @@ -83,7 +83,20 @@ class ExposedActorRepository( Actors.id eq id.id } .let(actorQueryMapper::map) - .first() + .firstOrNull() + } + } + + override suspend fun findByNameAndDomain(name: String, domain: String): Actor? { + return query { + Actors + .leftJoin(ActorsAlsoKnownAs, onColumn = { id }, otherColumn = { actorId }) + .selectAll() + .where { + Actors.name eq name and (Actors.domain eq domain) + } + .let(actorQueryMapper::map) + .firstOrNull() } } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt index 922a605f..574ea476 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt @@ -20,6 +20,7 @@ import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.post.* import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository +import dev.usbharu.hideout.core.infrastructure.exposed.QueryMapper import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.actorId import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.apId import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.content @@ -44,7 +45,10 @@ import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository -class ExposedPostRepository(override val domainEventPublisher: DomainEventPublisher) : +class ExposedPostRepository( + private val postQueryMapper: QueryMapper, + override val domainEventPublisher: DomainEventPublisher, +) : PostRepository, AbstractRepository(), DomainEventPublishableRepository { @@ -144,16 +148,23 @@ class ExposedPostRepository(override val domainEventPublisher: DomainEventPublis return posts } - override suspend fun findById(id: PostId): Post? { - query { - Posts.selectAll().where { + override suspend fun findById(id: PostId): Post? = query { + Posts + .selectAll() + .where { Posts.id eq id.id } - } + .let(postQueryMapper::map) + .first() } - override suspend fun findByActorId(id: ActorId): List { - TODO("Not yet implemented") + override suspend fun findByActorId(id: ActorId): List = query { + Posts + .selectAll() + .where { + actorId eq id.id + } + .let(postQueryMapper::map) } override suspend fun delete(post: Post) { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt new file mode 100644 index 00000000..1f8ae461 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.springframework + +import dev.usbharu.hideout.core.domain.service.userdetail.PasswordEncoder +import org.springframework.stereotype.Component + +@Component +class SpringSecurityPasswordEncoder(private val passwordEncoder: org.springframework.security.crypto.password.PasswordEncoder) : + PasswordEncoder { + override suspend fun encode(input: String): String { + return passwordEncoder.encode(input) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/resources/application.yml b/hideout-core/src/main/resources/application.yml index 52bc6eba..e5c023ec 100644 --- a/hideout-core/src/main/resources/application.yml +++ b/hideout-core/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: default-property-inclusion: always datasource: driver-class-name: org.postgresql.Driver - url: "jdbc:postgresql:hideout2" + url: "jdbc:postgresql:hideout3" username: "postgres" password: "" data: diff --git a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql index 0e81fca9..699a3357 100644 --- a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql +++ b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql @@ -41,20 +41,20 @@ create table if not exists actors url varchar(1000) not null unique, public_key varchar(10000) not null, private_key varchar(10000) null, - created_at bigint not null, + created_at timestamp not null, key_id varchar(1000) not null, "following" varchar(1000) null, followers varchar(1000) null, - "instance" bigint not null, + "instance" bigint not null, locked boolean not null, - following_count int not null, - followers_count int not null, + following_count int null, + followers_count int null, posts_count int not null, last_post_at timestamp null default null, - last_update_at timestamp not null, - suspend boolean not null, - move_to bigint null default null, - emojis varchar(3000) not null default '', + last_update_at timestamp not null, + suspend boolean not null, + move_to bigint null default null, + emojis varchar(3000) not null default '', unique ("name", "domain"), constraint fk_actors_instance__id foreign key ("instance") references instance (id) on delete restrict on update restrict, constraint fk_actors_actors__move_to foreign key ("move_to") references actors (id) on delete restrict on update restrict @@ -250,8 +250,9 @@ values (0, 'system', '', '', '', null, '', '', false, false, '', current_timesta 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, - last_post_at) -values (0, 'ghost', '', '', '', '', '', '', '', null, 0, '', '', '', 0, true, 0, 0, 0, null); + last_post_at, last_update_at, suspend, move_to, emojis) +values (0, '', '', '', '', '', '', '', '', null, current_timestamp, '', null, null, 0, true, null, null, 0, null, + current_timestamp, false, null, ''); create table if not exists deleted_actors ( diff --git a/libs.versions.toml b/libs.versions.toml index 4280a193..e8b1fed1 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -82,6 +82,7 @@ imageio-webp = { module = "com.twelvemonkeys.imageio:imageio-webp", version = "3 thumbnailator = { module = "net.coobird:thumbnailator", version = "0.4.20" } flyway-core = { module = "org.flywaydb:flyway-core" } +flyway-postgresql = { module = "org.flywaydb:flyway-database-postgresql", version = "10.14.0" } h2db = { module = "com.h2database:h2", version = "2.2.224" }