From 9ce7b6e6ddbc58dcfa125473df735f17a57d1259 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:47:38 +0900 Subject: [PATCH 001/116] =?UTF-8?q?fix:=20=E3=83=AA=E3=83=A2=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=82=92=E5=8F=96?= =?UTF-8?q?=E5=BE=97=E6=99=82=E3=81=AB=E4=BD=BF=E7=94=A8=E3=81=97=E3=81=9F?= =?UTF-8?q?URL=E3=81=A7url=E3=81=8C=E7=99=BB=E9=8C=B2=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=81=A6=E3=81=97=E3=81=BE=E3=81=86=E5=95=8F=E9=A1=8C=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/objects/user/APUserService.kt | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt index 9c0777e9..03de36a3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt @@ -80,26 +80,27 @@ class APUserServiceImpl( override suspend fun fetchPersonWithEntity(url: String, targetActor: String?): Pair { return try { val userEntity = userQueryService.findByUrl(url) + val id = userEntity.url return Person( type = emptyList(), name = userEntity.name, - id = url, + id = id, preferredUsername = userEntity.name, summary = userEntity.description, - inbox = "$url/inbox", - outbox = "$url/outbox", - url = url, + inbox = "$id/inbox", + outbox = "$id/outbox", + url = id, icon = Image( type = emptyList(), - name = "$url/icon.png", + name = "$id/icon.png", mediaType = "image/png", - url = "$url/icon.png" + url = "$id/icon.png" ), publicKey = Key( type = emptyList(), name = "Public Key", id = userEntity.keyId, - owner = url, + owner = id, publicKeyPem = userEntity.publicKey ), endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox"), @@ -109,17 +110,18 @@ class APUserServiceImpl( } catch (ignore: FailedToGetResourcesException) { val person = apResourceResolveService.resolve(url, null as Long?) + val id = person.id ?: throw IllegalActivityPubObjectException("id is null") person to userService.createRemoteUser( RemoteUserCreateDto( name = person.preferredUsername ?: throw IllegalActivityPubObjectException("preferredUsername is null"), - domain = url.substringAfter("://").substringBefore("/"), + domain = id.substringAfter("://").substringBefore("/"), screenName = (person.name ?: person.preferredUsername) ?: throw IllegalActivityPubObjectException("preferredUsername is null"), description = person.summary.orEmpty(), inbox = person.inbox ?: throw IllegalActivityPubObjectException("inbox is null"), outbox = person.outbox ?: throw IllegalActivityPubObjectException("outbox is null"), - url = url, + url = id, publicKey = person.publicKey?.publicKeyPem ?: throw IllegalActivityPubObjectException("publicKey is null"), keyId = person.publicKey?.id ?: throw IllegalActivityPubObjectException("publicKey keyId is null"), From 699dceea2e6a3ff022091e3f8136ba10670781a5 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:24:46 +0900 Subject: [PATCH 002/116] =?UTF-8?q?feat:=20PostgreSQL=E3=81=A7=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=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 --- .../resources/db/migration/V1__Init_DB.sql | 324 +++++++++--------- 1 file changed, 162 insertions(+), 162 deletions(-) diff --git a/src/main/resources/db/migration/V1__Init_DB.sql b/src/main/resources/db/migration/V1__Init_DB.sql index 34da6594..15a61994 100644 --- a/src/main/resources/db/migration/V1__Init_DB.sql +++ b/src/main/resources/db/migration/V1__Init_DB.sql @@ -1,188 +1,188 @@ -CREATE TABLE IF NOT EXISTS "INSTANCE" +create table if not exists instance ( - ID BIGINT PRIMARY KEY, - "NAME" VARCHAR(1000) NOT NULL, - DESCRIPTION VARCHAR(5000) NOT NULL, - URL VARCHAR(255) NOT NULL, - ICON_URL VARCHAR(255) NOT NULL, - SHARED_INBOX VARCHAR(255) NULL, - SOFTWARE VARCHAR(255) NOT NULL, - VERSION VARCHAR(255) NOT NULL, - IS_BLOCKED BOOLEAN NOT NULL, - IS_MUTED BOOLEAN NOT NULL, - MODERATION_NOTE VARCHAR(10000) NOT NULL, - CREATED_AT TIMESTAMP NOT NULL + id bigint primary key, + "name" varchar(1000) not null, + description varchar(5000) not null, + url varchar(255) not null, + icon_url varchar(255) not null, + shared_inbox varchar(255) null, + software varchar(255) not null, + version varchar(255) not null, + is_blocked boolean not null, + is_muted boolean not null, + moderation_note varchar(10000) not null, + created_at timestamp not null ); -CREATE TABLE IF NOT EXISTS USERS +create table if not exists users ( - ID BIGINT PRIMARY KEY, - "NAME" VARCHAR(300) NOT NULL, - "DOMAIN" VARCHAR(1000) NOT NULL, - SCREEN_NAME VARCHAR(300) NOT NULL, - DESCRIPTION VARCHAR(10000) NOT NULL, - PASSWORD VARCHAR(255) NULL, - INBOX VARCHAR(1000) NOT NULL, - OUTBOX VARCHAR(1000) NOT NULL, - URL VARCHAR(1000) NOT NULL, - PUBLIC_KEY VARCHAR(10000) NOT NULL, - PRIVATE_KEY VARCHAR(10000) NULL, - CREATED_AT BIGINT NOT NULL, - KEY_ID VARCHAR(1000) NOT NULL, - "FOLLOWING" VARCHAR(1000) NULL, - FOLLOWERS VARCHAR(1000) NULL, - "INSTANCE" BIGINT NULL, - CONSTRAINT FK_USERS_INSTANCE__ID FOREIGN KEY ("INSTANCE") REFERENCES "INSTANCE" (ID) ON DELETE RESTRICT ON UPDATE RESTRICT + id bigint primary key, + "name" varchar(300) not null, + "domain" varchar(1000) not null, + screen_name varchar(300) not null, + description varchar(10000) not null, + password varchar(255) null, + inbox varchar(1000) not null, + outbox varchar(1000) not null, + url varchar(1000) not null, + public_key varchar(10000) not null, + private_key varchar(10000) null, + created_at bigint not null, + key_id varchar(1000) not null, + "following" varchar(1000) null, + followers varchar(1000) null, + "instance" bigint null, + constraint fk_users_instance__id foreign key ("instance") references instance (id) on delete restrict on update restrict ); -CREATE TABLE IF NOT EXISTS FOLLOW_REQUESTS +create table if not exists follow_requests ( - ID BIGSERIAL PRIMARY KEY, - USER_ID BIGINT NOT NULL, - FOLLOWER_ID BIGINT NOT NULL, - CONSTRAINT FK_FOLLOW_REQUESTS_USER_ID__ID FOREIGN KEY (USER_ID) REFERENCES USERS (ID) ON DELETE RESTRICT ON UPDATE RESTRICT, - CONSTRAINT FK_FOLLOW_REQUESTS_FOLLOWER_ID__ID FOREIGN KEY (FOLLOWER_ID) REFERENCES USERS (ID) ON DELETE RESTRICT ON UPDATE RESTRICT + id bigserial primary key, + user_id bigint not null, + follower_id bigint not null, + constraint fk_follow_requests_user_id__id foreign key (user_id) references users (id) on delete restrict on update restrict, + constraint fk_follow_requests_follower_id__id foreign key (follower_id) references users (id) on delete restrict on update restrict ); -CREATE TABLE IF NOT EXISTS MEDIA +create table if not exists media ( - ID BIGINT PRIMARY KEY, - "NAME" VARCHAR(255) NOT NULL, - URL VARCHAR(255) NOT NULL, - REMOTE_URL VARCHAR(255) NULL, - THUMBNAIL_URL VARCHAR(255) NULL, - "TYPE" INT NOT NULL, - BLURHASH VARCHAR(255) NULL, - MIME_TYPE VARCHAR(255) NOT NULL, - DESCRIPTION VARCHAR(4000) NULL + id bigint primary key, + "name" varchar(255) not null, + url varchar(255) not null, + remote_url varchar(255) null, + thumbnail_url varchar(255) null, + "type" int not null, + blurhash varchar(255) null, + mime_type varchar(255) not null, + description varchar(4000) null ); -CREATE TABLE IF NOT EXISTS META_INFO +create table if not exists meta_info ( - ID BIGINT PRIMARY KEY, - VERSION VARCHAR(1000) NOT NULL, - KID VARCHAR(1000) NOT NULL, - JWT_PRIVATE_KEY VARCHAR(100000) NOT NULL, - JWT_PUBLIC_KEY VARCHAR(100000) NOT NULL + id bigint primary key, + version varchar(1000) not null, + kid varchar(1000) not null, + jwt_private_key varchar(100000) not null, + jwt_public_key varchar(100000) not null ); -CREATE TABLE IF NOT EXISTS POSTS +create table if not exists posts ( - ID BIGINT PRIMARY KEY, - USER_ID BIGINT NOT NULL, - OVERVIEW VARCHAR(100) NULL, - TEXT VARCHAR(3000) NOT NULL, - CREATED_AT BIGINT NOT NULL, - VISIBILITY INT DEFAULT 0 NOT NULL, - URL VARCHAR(500) NOT NULL, - REPOST_ID BIGINT NULL, - REPLY_ID BIGINT NULL, - "SENSITIVE" BOOLEAN DEFAULT FALSE NOT NULL, - AP_ID VARCHAR(100) NOT NULL + id bigint primary key, + user_id bigint not null, + overview varchar(100) null, + text varchar(3000) not null, + created_at bigint not null, + visibility int default 0 not null, + url varchar(500) not null, + repost_id bigint null, + reply_id bigint null, + "sensitive" boolean default false not null, + ap_id varchar(100) not null ); -ALTER TABLE POSTS - ADD CONSTRAINT FK_POSTS_USERID__ID FOREIGN KEY (USER_ID) REFERENCES USERS (ID) ON DELETE RESTRICT ON UPDATE RESTRICT; -ALTER TABLE POSTS - ADD CONSTRAINT FK_POSTS_REPOSTID__ID FOREIGN KEY (REPOST_ID) REFERENCES POSTS (ID) ON DELETE RESTRICT ON UPDATE RESTRICT; -ALTER TABLE POSTS - ADD CONSTRAINT FK_POSTS_REPLYID__ID FOREIGN KEY (REPLY_ID) REFERENCES POSTS (ID) ON DELETE RESTRICT ON UPDATE RESTRICT; -CREATE TABLE IF NOT EXISTS POSTS_MEDIA +alter table posts + add constraint fk_posts_userid__id foreign key (user_id) references users (id) on delete restrict on update restrict; +alter table posts + add constraint fk_posts_repostid__id foreign key (repost_id) references posts (id) on delete restrict on update restrict; +alter table posts + add constraint fk_posts_replyid__id foreign key (reply_id) references posts (id) on delete restrict on update restrict; +create table if not exists posts_media ( - POST_ID BIGINT, - MEDIA_ID BIGINT, - CONSTRAINT pk_PostsMedia PRIMARY KEY (POST_ID, MEDIA_ID) + post_id bigint, + media_id bigint, + constraint pk_postsmedia primary key (post_id, media_id) ); -ALTER TABLE POSTS_MEDIA - ADD CONSTRAINT FK_POSTS_MEDIA_POST_ID__ID FOREIGN KEY (POST_ID) REFERENCES POSTS (ID) ON DELETE CASCADE ON UPDATE CASCADE; -ALTER TABLE POSTS_MEDIA - ADD CONSTRAINT FK_POSTS_MEDIA_MEDIA_ID__ID FOREIGN KEY (MEDIA_ID) REFERENCES MEDIA (ID) ON DELETE CASCADE ON UPDATE CASCADE; -CREATE TABLE IF NOT EXISTS REACTIONS +alter table posts_media + add constraint fk_posts_media_post_id__id foreign key (post_id) references posts (id) on delete cascade on update cascade; +alter table posts_media + add constraint fk_posts_media_media_id__id foreign key (media_id) references media (id) on delete cascade on update cascade; +create table if not exists reactions ( - ID BIGSERIAL PRIMARY KEY, - EMOJI_ID BIGINT NOT NULL, - POST_ID BIGINT NOT NULL, - USER_ID BIGINT NOT NULL + id bigserial primary key, + emoji_id bigint not null, + post_id bigint not null, + user_id bigint not null ); -ALTER TABLE REACTIONS - ADD CONSTRAINT FK_REACTIONS_POST_ID__ID FOREIGN KEY (POST_ID) REFERENCES POSTS (ID) ON DELETE RESTRICT ON UPDATE RESTRICT; -ALTER TABLE REACTIONS - ADD CONSTRAINT FK_REACTIONS_USER_ID__ID FOREIGN KEY (USER_ID) REFERENCES USERS (ID) ON DELETE RESTRICT ON UPDATE RESTRICT; -CREATE TABLE IF NOT EXISTS TIMELINES +alter table reactions + add constraint fk_reactions_post_id__id foreign key (post_id) references posts (id) on delete restrict on update restrict; +alter table reactions + add constraint fk_reactions_user_id__id foreign key (user_id) references users (id) on delete restrict on update restrict; +create table if not exists timelines ( - ID BIGINT PRIMARY KEY, - USER_ID BIGINT NOT NULL, - TIMELINE_ID BIGINT NOT NULL, - POST_ID BIGINT NOT NULL, - POST_USER_ID BIGINT NOT NULL, - CREATED_AT BIGINT NOT NULL, - REPLY_ID BIGINT NULL, - REPOST_ID BIGINT NULL, - VISIBILITY INT NOT NULL, - "SENSITIVE" BOOLEAN NOT NULL, - IS_LOCAL BOOLEAN NOT NULL, - IS_PURE_REPOST BOOLEAN NOT NULL, - MEDIA_IDS VARCHAR(255) NOT NULL + id bigint primary key, + user_id bigint not null, + timeline_id bigint not null, + post_id bigint not null, + post_user_id bigint not null, + created_at bigint not null, + reply_id bigint null, + repost_id bigint null, + visibility int not null, + "sensitive" boolean not null, + is_local boolean not null, + is_pure_repost boolean not null, + media_ids varchar(255) not null ); -CREATE TABLE IF NOT EXISTS USERS_FOLLOWERS +create table if not exists users_followers ( - ID BIGSERIAL PRIMARY KEY, - USER_ID BIGINT NOT NULL, - FOLLOWER_ID BIGINT NOT NULL, - CONSTRAINT FK_USERS_FOLLOWERS_USER_ID__ID FOREIGN KEY (USER_ID) REFERENCES USERS (ID) ON DELETE RESTRICT ON UPDATE RESTRICT, - CONSTRAINT FK_USERS_FOLLOWERS_FOLLOWER_ID__ID FOREIGN KEY (FOLLOWER_ID) REFERENCES USERS (ID) ON DELETE RESTRICT ON UPDATE RESTRICT + id bigserial primary key, + user_id bigint not null, + follower_id bigint not null, + constraint fk_users_followers_user_id__id foreign key (user_id) references users (id) on delete restrict on update restrict, + constraint fk_users_followers_follower_id__id foreign key (follower_id) references users (id) on delete restrict on update restrict ); -CREATE TABLE IF NOT EXISTS APPLICATION_AUTHORIZATION +create table if not exists application_authorization ( - ID VARCHAR(255) PRIMARY KEY, - REGISTERED_CLIENT_ID VARCHAR(255) NOT NULL, - PRINCIPAL_NAME VARCHAR(255) NOT NULL, - AUTHORIZATION_GRANT_TYPE VARCHAR(255) NOT NULL, - AUTHORIZED_SCOPES VARCHAR(1000) DEFAULT NULL NULL, - "ATTRIBUTES" VARCHAR(4000) DEFAULT NULL NULL, - "STATE" VARCHAR(500) DEFAULT NULL NULL, - AUTHORIZATION_CODE_VALUE VARCHAR(4000) DEFAULT NULL NULL, - AUTHORIZATION_CODE_ISSUED_AT TIMESTAMP DEFAULT NULL NULL, - AUTHORIZATION_CODE_EXPIRES_AT TIMESTAMP DEFAULT NULL NULL, - AUTHORIZATION_CODE_METADATA VARCHAR(2000) DEFAULT NULL NULL, - ACCESS_TOKEN_VALUE VARCHAR(4000) DEFAULT NULL NULL, - ACCESS_TOKEN_ISSUED_AT TIMESTAMP DEFAULT NULL NULL, - ACCESS_TOKEN_EXPIRES_AT TIMESTAMP DEFAULT NULL NULL, - ACCESS_TOKEN_METADATA VARCHAR(2000) DEFAULT NULL NULL, - ACCESS_TOKEN_TYPE VARCHAR(255) DEFAULT NULL NULL, - ACCESS_TOKEN_SCOPES VARCHAR(1000) DEFAULT NULL NULL, - REFRESH_TOKEN_VALUE VARCHAR(4000) DEFAULT NULL NULL, - REFRESH_TOKEN_ISSUED_AT TIMESTAMP DEFAULT NULL NULL, - REFRESH_TOKEN_EXPIRES_AT TIMESTAMP DEFAULT NULL NULL, - REFRESH_TOKEN_METADATA VARCHAR(2000) DEFAULT NULL NULL, - OIDC_ID_TOKEN_VALUE VARCHAR(4000) DEFAULT NULL NULL, - OIDC_ID_TOKEN_ISSUED_AT TIMESTAMP DEFAULT NULL NULL, - OIDC_ID_TOKEN_EXPIRES_AT TIMESTAMP DEFAULT NULL NULL, - OIDC_ID_TOKEN_METADATA VARCHAR(2000) DEFAULT NULL NULL, - OIDC_ID_TOKEN_CLAIMS VARCHAR(2000) DEFAULT NULL NULL, - USER_CODE_VALUE VARCHAR(4000) DEFAULT NULL NULL, - USER_CODE_ISSUED_AT TIMESTAMP DEFAULT NULL NULL, - USER_CODE_EXPIRES_AT TIMESTAMP DEFAULT NULL NULL, - USER_CODE_METADATA VARCHAR(2000) DEFAULT NULL NULL, - DEVICE_CODE_VALUE VARCHAR(4000) DEFAULT NULL NULL, - DEVICE_CODE_ISSUED_AT TIMESTAMP DEFAULT NULL NULL, - DEVICE_CODE_EXPIRES_AT TIMESTAMP DEFAULT NULL NULL, - DEVICE_CODE_METADATA VARCHAR(2000) DEFAULT NULL NULL + id varchar(255) primary key, + registered_client_id varchar(255) not null, + principal_name varchar(255) not null, + authorization_grant_type varchar(255) not null, + authorized_scopes varchar(1000) default null null, + "attributes" varchar(4000) default null null, + "state" varchar(500) default null null, + authorization_code_value varchar(4000) default null null, + authorization_code_issued_at timestamp default null null, + authorization_code_expires_at timestamp default null null, + authorization_code_metadata varchar(2000) default null null, + access_token_value varchar(4000) default null null, + access_token_issued_at timestamp default null null, + access_token_expires_at timestamp default null null, + access_token_metadata varchar(2000) default null null, + access_token_type varchar(255) default null null, + access_token_scopes varchar(1000) default null null, + refresh_token_value varchar(4000) default null null, + refresh_token_issued_at timestamp default null null, + refresh_token_expires_at timestamp default null null, + refresh_token_metadata varchar(2000) default null null, + oidc_id_token_value varchar(4000) default null null, + oidc_id_token_issued_at timestamp default null null, + oidc_id_token_expires_at timestamp default null null, + oidc_id_token_metadata varchar(2000) default null null, + oidc_id_token_claims varchar(2000) default null null, + user_code_value varchar(4000) default null null, + user_code_issued_at timestamp default null null, + user_code_expires_at timestamp default null null, + user_code_metadata varchar(2000) default null null, + device_code_value varchar(4000) default null null, + device_code_issued_at timestamp default null null, + device_code_expires_at timestamp default null null, + device_code_metadata varchar(2000) default null null ); -CREATE TABLE IF NOT EXISTS OAUTH2_AUTHORIZATION_CONSENT +create table if not exists oauth2_authorization_consent ( - REGISTERED_CLIENT_ID VARCHAR(100), - PRINCIPAL_NAME VARCHAR(200), - AUTHORITIES VARCHAR(1000) NOT NULL, - CONSTRAINT pk_oauth2_authorization_consent PRIMARY KEY (REGISTERED_CLIENT_ID, PRINCIPAL_NAME) + registered_client_id varchar(100), + principal_name varchar(200), + authorities varchar(1000) not null, + constraint pk_oauth2_authorization_consent primary key (registered_client_id, principal_name) ); -CREATE TABLE IF NOT EXISTS REGISTERED_CLIENT +create table if not exists registered_client ( - ID VARCHAR(100) PRIMARY KEY, - CLIENT_ID VARCHAR(100) NOT NULL, - CLIENT_ID_ISSUED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - CLIENT_SECRET VARCHAR(200) DEFAULT NULL NULL, - CLIENT_SECRET_EXPIRES_AT TIMESTAMP DEFAULT NULL NULL, - CLIENT_NAME VARCHAR(200) NOT NULL, - CLIENT_AUTHENTICATION_METHODS VARCHAR(1000) NOT NULL, - AUTHORIZATION_GRANT_TYPES VARCHAR(1000) NOT NULL, - REDIRECT_URIS VARCHAR(1000) DEFAULT NULL NULL, - POST_LOGOUT_REDIRECT_URIS VARCHAR(1000) DEFAULT NULL NULL, - SCOPES VARCHAR(1000) NOT NULL, - CLIENT_SETTINGS VARCHAR(2000) NOT NULL, - TOKEN_SETTINGS VARCHAR(2000) NOT NULL + id varchar(100) primary key, + client_id varchar(100) not null, + client_id_issued_at timestamp default current_timestamp not null, + client_secret varchar(200) default null null, + client_secret_expires_at timestamp default null null, + client_name varchar(200) not null, + client_authentication_methods varchar(1000) not null, + authorization_grant_types varchar(1000) not null, + redirect_uris varchar(1000) default null null, + post_logout_redirect_uris varchar(1000) default null null, + scopes varchar(1000) not null, + client_settings varchar(2000) not null, + token_settings varchar(2000) not null ) From 8fa96b771ecf2846a6eda2f6c69cc4bc682b61ed Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:25:08 +0900 Subject: [PATCH 003/116] =?UTF-8?q?feat:=20PostgreSQL=E3=81=A7=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=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 --- src/main/resources/application.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2d0a6c82..c9c2604f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -18,9 +18,11 @@ spring: WRITE_DATES_AS_TIMESTAMPS: false default-property-inclusion: always datasource: - driver-class-name: org.h2.Driver - url: "jdbc:h2:./test-dev4;MODE=POSTGRESQL;TRACE_LEVEL_FILE=4" - username: "" + hikari: + transaction-isolation: "TRANSACTION_SERIALIZABLE" + driver-class-name: org.postgresql.Driver + url: "jdbc:postgresql:hideout2" + username: "postgres" password: "" # data: # mongodb: From 4655156e35fe31a569af4e1996e6732e9876d94f Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:25:31 +0900 Subject: [PATCH 004/116] =?UTF-8?q?fix:=20instance=E3=81=AE=E4=B8=80?= =?UTF-8?q?=E6=84=8F=E6=80=A7=E3=82=92=E4=BF=9D=E8=A8=BC=E3=81=A7=E3=81=8D?= =?UTF-8?q?=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/application/config/SecurityConfig.kt | 9 +++++++-- .../exposedquery/InstanceQueryServiceImpl.kt | 2 +- .../exposedrepository/InstanceRepositoryImpl.kt | 4 ++-- .../httpsignature/HttpSignatureFilter.kt | 14 +++++++++++++- .../core/service/instance/InstanceService.kt | 2 +- 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index a5762bb1..30b505c2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -6,6 +6,7 @@ import com.nimbusds.jose.jwk.RSAKey import com.nimbusds.jose.jwk.source.ImmutableJWKSet import com.nimbusds.jose.jwk.source.JWKSource import com.nimbusds.jose.proc.SecurityContext +import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureFilter import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService @@ -113,8 +114,12 @@ class SecurityConfig { } @Bean - fun getHttpSignatureFilter(authenticationManager: AuthenticationManager): HttpSignatureFilter { - val httpSignatureFilter = HttpSignatureFilter(DefaultSignatureHeaderParser()) + fun getHttpSignatureFilter( + authenticationManager: AuthenticationManager, + transaction: Transaction, + apUserService: APUserService + ): HttpSignatureFilter { + val httpSignatureFilter = HttpSignatureFilter(DefaultSignatureHeaderParser(), transaction, apUserService) httpSignatureFilter.setAuthenticationManager(authenticationManager) httpSignatureFilter.setContinueFilterChainOnUnsuccessfulAuthentication(false) val authenticationEntryPointFailureHandler = diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/InstanceQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/InstanceQueryServiceImpl.kt index 587f57a1..c7d276ef 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/InstanceQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/InstanceQueryServiceImpl.kt @@ -12,5 +12,5 @@ import dev.usbharu.hideout.core.domain.model.instance.Instance as InstanceEntity @Repository class InstanceQueryServiceImpl : InstanceQueryService { override suspend fun findByUrl(url: String): InstanceEntity = Instance.select { Instance.url eq url } - .singleOr { FailedToGetResourcesException("url is doesn't exist") }.toInstance() + .singleOr { FailedToGetResourcesException("$url is doesn't exist", it) }.toInstance() } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt index edd79195..a7d8aa9b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt @@ -79,9 +79,9 @@ object Instance : Table("instance") { val id = long("id") val name = varchar("name", 1000) val description = varchar("description", 5000) - val url = varchar("url", 255) + val url = varchar("url", 255).uniqueIndex() val iconUrl = varchar("icon_url", 255) - val sharedInbox = varchar("shared_inbox", 255).nullable() + val sharedInbox = varchar("shared_inbox", 255).nullable().uniqueIndex() val software = varchar("software", 255) val version = varchar("version", 255) val isBlocked = bool("is_blocked") diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt index 8b3c1b11..8d03463c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt @@ -1,14 +1,21 @@ package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature +import dev.usbharu.hideout.activitypub.service.objects.user.APUserService +import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest import dev.usbharu.httpsignature.verify.SignatureHeaderParser import jakarta.servlet.http.HttpServletRequest +import kotlinx.coroutines.runBlocking import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter import java.net.URL -class HttpSignatureFilter(private val httpSignatureHeaderParser: SignatureHeaderParser) : +class HttpSignatureFilter( + private val httpSignatureHeaderParser: SignatureHeaderParser, + private val transaction: Transaction, + private val apUserService: APUserService +) : AbstractPreAuthenticatedProcessingFilter() { override fun getPreAuthenticatedPrincipal(request: HttpServletRequest?): Any? { val headersList = request?.headerNames?.toList().orEmpty() @@ -23,6 +30,11 @@ class HttpSignatureFilter(private val httpSignatureHeaderParser: SignatureHeader } catch (_: RuntimeException) { return "" } + runBlocking { + transaction.transaction { + apUserService.fetchPerson(signature.keyId) + } + } return signature.keyId } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt index 4b0e2640..41459964 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt @@ -31,7 +31,7 @@ class InstanceServiceImpl( val resolveInstanceUrl = u.protocol + "://" + u.host try { - return instanceQueryService.findByUrl(url) + return instanceQueryService.findByUrl(resolveInstanceUrl) } catch (e: FailedToGetResourcesException) { logger.info("Instance not found. try fetch instance info. url: {}", resolveInstanceUrl) logger.debug("Failed to get resources. url: {}", resolveInstanceUrl, e) From 3ac07822467b4ebd27a6d58b3d3fde131bf036a2 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:40:57 +0900 Subject: [PATCH 005/116] =?UTF-8?q?fix:=20user=E3=81=AE=E4=B8=80=E6=84=8F?= =?UTF-8?q?=E6=80=A7=E3=82=92=E4=BF=9D=E8=A8=BC=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/objects/user/APUserService.kt | 30 +++++++++++++++++++ .../application/config/SecurityConfig.kt | 6 ++-- .../exposedrepository/UserRepositoryImpl.kt | 2 +- .../httpsignature/HttpSignatureFilter.kt | 11 +++++-- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt index 03de36a3..9e8cb4b8 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt @@ -111,6 +111,36 @@ class APUserServiceImpl( val person = apResourceResolveService.resolve(url, null as Long?) val id = person.id ?: throw IllegalActivityPubObjectException("id is null") + try { + val userEntity = userQueryService.findByUrl(id) + return Person( + type = emptyList(), + name = userEntity.name, + id = id, + preferredUsername = userEntity.name, + summary = userEntity.description, + inbox = "$id/inbox", + outbox = "$id/outbox", + url = id, + icon = Image( + type = emptyList(), + name = "$id/icon.png", + mediaType = "image/png", + url = "$id/icon.png" + ), + publicKey = Key( + type = emptyList(), + name = "Public Key", + id = userEntity.keyId, + owner = id, + publicKeyPem = userEntity.publicKey + ), + endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox"), + followers = userEntity.followers, + following = userEntity.following + ) to userEntity + } catch (_: FailedToGetResourcesException) { + } person to userService.createRemoteUser( RemoteUserCreateDto( name = person.preferredUsername diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index 30b505c2..ecdc3ac9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -117,9 +117,11 @@ class SecurityConfig { fun getHttpSignatureFilter( authenticationManager: AuthenticationManager, transaction: Transaction, - apUserService: APUserService + apUserService: APUserService, + userQueryService: UserQueryService ): HttpSignatureFilter { - val httpSignatureFilter = HttpSignatureFilter(DefaultSignatureHeaderParser(), transaction, apUserService) + val httpSignatureFilter = + HttpSignatureFilter(DefaultSignatureHeaderParser(), transaction, apUserService, userQueryService) httpSignatureFilter.setAuthenticationManager(authenticationManager) httpSignatureFilter.setContinueFilterChainOnUnsuccessfulAuthentication(false) val authenticationEntryPointFailureHandler = diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserRepositoryImpl.kt index 5cd94ccf..ecd910a6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserRepositoryImpl.kt @@ -17,7 +17,7 @@ class UserRepositoryImpl( UserRepository { override suspend fun save(user: User): User { - val singleOrNull = Users.select { Users.id eq user.id or (Users.url eq user.url) }.empty() + val singleOrNull = Users.select { Users.id eq user.id }.empty() if (singleOrNull) { Users.insert { it[id] = user.id diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt index 8d03463c..6a68e267 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt @@ -2,6 +2,8 @@ package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest @@ -14,7 +16,8 @@ import java.net.URL class HttpSignatureFilter( private val httpSignatureHeaderParser: SignatureHeaderParser, private val transaction: Transaction, - private val apUserService: APUserService + private val apUserService: APUserService, + private val userQueryService: UserQueryService ) : AbstractPreAuthenticatedProcessingFilter() { override fun getPreAuthenticatedPrincipal(request: HttpServletRequest?): Any? { @@ -32,7 +35,11 @@ class HttpSignatureFilter( } runBlocking { transaction.transaction { - apUserService.fetchPerson(signature.keyId) + try { + userQueryService.findByKeyId(signature.keyId) + } catch (e: FailedToGetResourcesException) { + apUserService.fetchPerson(signature.keyId) + } } } return signature.keyId From e71a1f74a33c81d3846115fa372cd51fb08dd566 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:03:12 +0900 Subject: [PATCH 006/116] =?UTF-8?q?fix:=20=E3=83=88=E3=83=A9=E3=83=B3?= =?UTF-8?q?=E3=82=B6=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=E3=82=92=E3=81=82=E3=82=8B=E7=A8=8B=E5=BA=A6=E8=A7=A3?= =?UTF-8?q?=E6=B1=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/exposed/ExposedTransaction.kt | 3 +-- src/main/resources/application.yml | 2 -- src/main/resources/db/migration/V1__Init_DB.sql | 13 +++++++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt index 4dc8316b..3438d428 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt @@ -4,12 +4,11 @@ import dev.usbharu.hideout.application.external.Transaction import kotlinx.coroutines.slf4j.MDCContext import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.springframework.stereotype.Service -import java.sql.Connection @Service class ExposedTransaction : Transaction { override suspend fun transaction(block: suspend () -> T): T { - return newSuspendedTransaction(MDCContext(), transactionIsolation = Connection.TRANSACTION_SERIALIZABLE) { + return newSuspendedTransaction(MDCContext()) { block() } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c9c2604f..daff34db 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -18,8 +18,6 @@ spring: WRITE_DATES_AS_TIMESTAMPS: false default-property-inclusion: always datasource: - hikari: - transaction-isolation: "TRANSACTION_SERIALIZABLE" driver-class-name: org.postgresql.Driver url: "jdbc:postgresql:hideout2" username: "postgres" diff --git a/src/main/resources/db/migration/V1__Init_DB.sql b/src/main/resources/db/migration/V1__Init_DB.sql index 15a61994..4ea80255 100644 --- a/src/main/resources/db/migration/V1__Init_DB.sql +++ b/src/main/resources/db/migration/V1__Init_DB.sql @@ -3,9 +3,9 @@ create table if not exists instance id bigint primary key, "name" varchar(1000) not null, description varchar(5000) not null, - url varchar(255) not null, + url varchar(255) not null unique, icon_url varchar(255) not null, - shared_inbox varchar(255) null, + shared_inbox varchar(255) null unique, software varchar(255) not null, version varchar(255) not null, is_blocked boolean not null, @@ -21,9 +21,9 @@ create table if not exists users screen_name varchar(300) not null, description varchar(10000) not null, password varchar(255) null, - inbox varchar(1000) not null, - outbox varchar(1000) not null, - url varchar(1000) not null, + inbox varchar(1000) not null unique, + outbox varchar(1000) not null unique, + url varchar(1000) not null unique, public_key varchar(10000) not null, private_key varchar(10000) null, created_at bigint not null, @@ -31,6 +31,7 @@ create table if not exists users "following" varchar(1000) null, followers varchar(1000) null, "instance" bigint null, + unique (name, domain), constraint fk_users_instance__id foreign key ("instance") references instance (id) on delete restrict on update restrict ); create table if not exists follow_requests @@ -73,7 +74,7 @@ create table if not exists posts repost_id bigint null, reply_id bigint null, "sensitive" boolean default false not null, - ap_id varchar(100) not null + ap_id varchar(100) not null unique ); alter table posts add constraint fk_posts_userid__id foreign key (user_id) references users (id) on delete restrict on update restrict; From 25fbb12e5b54c65df1b6a5fcd4e02786c2a86ce7 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:18:34 +0900 Subject: [PATCH 007/116] test: fix test --- src/intTest/resources/application.yml | 2 ++ .../service/common/APResourceResolveServiceImpl.kt | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/intTest/resources/application.yml b/src/intTest/resources/application.yml index 9760e828..258014ca 100644 --- a/src/intTest/resources/application.yml +++ b/src/intTest/resources/application.yml @@ -17,6 +17,8 @@ hideout: secret-key: "" spring: + flyway: + enabled: false datasource: driver-class-name: org.h2.Driver url: "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1" diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt index 81b7aec3..85099d84 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt @@ -39,9 +39,8 @@ class APResourceResolveServiceImpl( return (cacheManager.getOrWait(key) as APResolveResponse).objects } - private suspend fun runResolve(url: String, singer: User?, clazz: Class): ResolveResponse { - return APResolveResponse(apRequestService.apGet(url, singer, clazz)) - } + private suspend fun runResolve(url: String, singer: User?, clazz: Class): ResolveResponse = + APResolveResponse(apRequestService.apGet(url, singer, clazz)) private fun genCacheKey(url: String, singerId: Long?): String { if (singerId != null) { From e4eaef7277cf9d95bdedb45b963557f4e9a678c9 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:58:13 +0900 Subject: [PATCH 008/116] =?UTF-8?q?feat:=20inbox=E3=82=92=E3=82=B8?= =?UTF-8?q?=E3=83=A7=E3=83=96=E3=82=AD=E3=83=A5=E3=83=BC=E3=81=AB=E8=BC=89?= =?UTF-8?q?=E3=81=9B=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/service/common/APService.kt | 26 +++++------- .../service/common/ApJobServiceImpl.kt | 40 ++++++++++++++++++- .../hideout/core/external/job/HideoutJob.kt | 6 +++ 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt index c7df1df2..78c2a52a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt @@ -2,16 +2,17 @@ package dev.usbharu.hideout.activitypub.service.common import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue import dev.usbharu.hideout.activitypub.domain.exception.JsonParseException -import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubResponse +import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.activitypub.service.activity.accept.APAcceptService import dev.usbharu.hideout.activitypub.service.activity.create.APCreateService import dev.usbharu.hideout.activitypub.service.activity.delete.APReceiveDeleteService import dev.usbharu.hideout.activitypub.service.activity.follow.APReceiveFollowService import dev.usbharu.hideout.activitypub.service.activity.like.APLikeService import dev.usbharu.hideout.activitypub.service.activity.undo.APUndoService +import dev.usbharu.hideout.core.external.job.InboxJob +import dev.usbharu.hideout.core.service.job.JobQueueParentService import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Qualifier @@ -182,7 +183,8 @@ class APServiceImpl( private val apCreateService: APCreateService, private val apLikeService: APLikeService, private val apReceiveDeleteService: APReceiveDeleteService, - @Qualifier("activitypub") private val objectMapper: ObjectMapper + @Qualifier("activitypub") private val objectMapper: ObjectMapper, + private val jobQueueParentService: JobQueueParentService ) : APService { val logger: Logger = LoggerFactory.getLogger(APServiceImpl::class.java) @@ -227,20 +229,10 @@ class APServiceImpl( @Suppress("CyclomaticComplexMethod", "NotImplementedDeclaration") override suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse { logger.debug("process activity: {}", type) - return when (type) { - ActivityType.Accept -> apAcceptService.receiveAccept(objectMapper.readValue(json)) - ActivityType.Follow -> - apReceiveFollowService - .receiveFollow(objectMapper.readValue(json, Follow::class.java)) - - ActivityType.Create -> apCreateService.receiveCreate(objectMapper.readValue(json)) - ActivityType.Like -> apLikeService.receiveLike(objectMapper.readValue(json)) - ActivityType.Undo -> apUndoService.receiveUndo(objectMapper.readValue(json)) - ActivityType.Delete -> apReceiveDeleteService.receiveDelete(objectMapper.readValue(json)) - - else -> { - throw IllegalArgumentException("$type is not supported.") - } + jobQueueParentService.schedule(InboxJob) { + props[it.json] = json + props[it.type] = type.name } + return ActivityPubStringResponse(message = "") } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt index 7057ccc5..13d23f57 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt @@ -1,19 +1,37 @@ package dev.usbharu.hideout.activitypub.service.common +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.service.activity.accept.APAcceptServiceImpl +import dev.usbharu.hideout.activitypub.service.activity.create.APCreateServiceImpl +import dev.usbharu.hideout.activitypub.service.activity.delete.APReceiveDeleteServiceImpl import dev.usbharu.hideout.activitypub.service.activity.follow.APReceiveFollowJobService +import dev.usbharu.hideout.activitypub.service.activity.follow.APReceiveFollowServiceImpl +import dev.usbharu.hideout.activitypub.service.activity.like.APLikeServiceImpl import dev.usbharu.hideout.activitypub.service.activity.like.ApReactionJobService +import dev.usbharu.hideout.activitypub.service.activity.undo.APUndoServiceImpl import dev.usbharu.hideout.activitypub.service.objects.note.ApNoteJobService import dev.usbharu.hideout.core.external.job.* + import kjob.core.dsl.JobContextWithProps import kjob.core.job.JobProps import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Service @Service class ApJobServiceImpl( private val apReceiveFollowJobService: APReceiveFollowJobService, private val apNoteJobService: ApNoteJobService, - private val apReactionJobService: ApReactionJobService + private val apReactionJobService: ApReactionJobService, + private val APAcceptServiceImpl: APAcceptServiceImpl, + private val APReceiveFollowServiceImpl: APReceiveFollowServiceImpl, + private val APCreateServiceImpl: APCreateServiceImpl, + private val APLikeServiceImpl: APLikeServiceImpl, + private val APUndoServiceImpl: APUndoServiceImpl, + private val APReceiveDeleteServiceImpl: APReceiveDeleteServiceImpl, + @Qualifier("activitypub") private val objectMapper: ObjectMapper ) : ApJobService { @Suppress("REDUNDANT_ELSE_IN_WHEN") override suspend fun processActivity(job: JobContextWithProps, hideoutJob: HideoutJob) { @@ -22,6 +40,26 @@ class ApJobServiceImpl( @Suppress("ElseCaseInsteadOfExhaustiveWhen") // Springで作成されるプロキシの都合上パターンマッチングが壊れるので必須 when (hideoutJob) { + is InboxJob -> { + val typeString = (job.props as JobProps)[InboxJob.type] + val json = (job.props as JobProps)[InboxJob.json] + val type = ActivityType.valueOf(typeString) + when (type) { + ActivityType.Accept -> APAcceptServiceImpl.receiveAccept(objectMapper.readValue(json)) + ActivityType.Follow -> + APReceiveFollowServiceImpl + .receiveFollow(objectMapper.readValue(json, Follow::class.java)) + + ActivityType.Create -> APCreateServiceImpl.receiveCreate(objectMapper.readValue(json)) + ActivityType.Like -> APLikeServiceImpl.receiveLike(objectMapper.readValue(json)) + ActivityType.Undo -> APUndoServiceImpl.receiveUndo(objectMapper.readValue(json)) + ActivityType.Delete -> APReceiveDeleteServiceImpl.receiveDelete(objectMapper.readValue(json)) + + else -> { + throw IllegalArgumentException("$type is not supported.") + } + } + } is ReceiveFollowJob -> { apReceiveFollowJobService.receiveFollowJob( job.props as JobProps diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt index 62f989d0..94fa81da 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt @@ -36,3 +36,9 @@ object DeliverRemoveReactionJob : HideoutJob("DeliverRemoveReactionJob") { val actor: Prop = string("actor") val like: Prop = string("like") } + +@Component +object InboxJob : HideoutJob("InboxJob") { + val json = string("json") + val type = string("type") +} From 65b37cbb5e85c7dfa0c85ec9fc6e0411cbefbb29 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:00:53 +0900 Subject: [PATCH 009/116] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E4=BE=9D=E5=AD=98=E9=96=A2=E4=BF=82=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/activitypub/service/common/APService.kt | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt index 78c2a52a..2db01861 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt @@ -5,12 +5,6 @@ import com.fasterxml.jackson.databind.ObjectMapper import dev.usbharu.hideout.activitypub.domain.exception.JsonParseException import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubResponse import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse -import dev.usbharu.hideout.activitypub.service.activity.accept.APAcceptService -import dev.usbharu.hideout.activitypub.service.activity.create.APCreateService -import dev.usbharu.hideout.activitypub.service.activity.delete.APReceiveDeleteService -import dev.usbharu.hideout.activitypub.service.activity.follow.APReceiveFollowService -import dev.usbharu.hideout.activitypub.service.activity.like.APLikeService -import dev.usbharu.hideout.activitypub.service.activity.undo.APUndoService import dev.usbharu.hideout.core.external.job.InboxJob import dev.usbharu.hideout.core.service.job.JobQueueParentService import org.slf4j.Logger @@ -177,12 +171,6 @@ enum class ExtendedVocabulary { @Service class APServiceImpl( - private val apReceiveFollowService: APReceiveFollowService, - private val apUndoService: APUndoService, - private val apAcceptService: APAcceptService, - private val apCreateService: APCreateService, - private val apLikeService: APLikeService, - private val apReceiveDeleteService: APReceiveDeleteService, @Qualifier("activitypub") private val objectMapper: ObjectMapper, private val jobQueueParentService: JobQueueParentService ) : APService { From 986d16f442a41921123a7eb9bdeb8fed5fdb7712 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 22 Nov 2023 00:07:38 +0900 Subject: [PATCH 010/116] =?UTF-8?q?feat:=20=E3=82=B8=E3=83=A7=E3=83=96?= =?UTF-8?q?=E3=82=AD=E3=83=A5=E3=83=BC=E3=81=A7HTTP=20Signature=E3=81=AE?= =?UTF-8?q?=E6=A4=9C=E8=A8=BC=E3=82=92=E8=A1=8C=E3=81=86=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/inbox/InboxControllerImpl.kt | 38 ++++++++++++++++- .../activitypub/service/common/APService.kt | 19 ++++++++- .../service/common/ApJobServiceImpl.kt | 42 ++++++++++++++++++- .../application/config/HttpSignatureConfig.kt | 20 +++++++++ .../hideout/core/external/job/HideoutJob.kt | 2 + 5 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/application/config/HttpSignatureConfig.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt index 04f1d6f3..fc8acf7f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt @@ -1,16 +1,28 @@ package dev.usbharu.hideout.activitypub.interfaces.api.inbox import dev.usbharu.hideout.activitypub.service.common.APService +import dev.usbharu.httpsignature.common.HttpHeaders +import dev.usbharu.httpsignature.common.HttpMethod +import dev.usbharu.httpsignature.common.HttpRequest import org.slf4j.LoggerFactory import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RestController +import org.springframework.web.context.request.RequestContextHolder +import org.springframework.web.context.request.ServletRequestAttributes +import java.net.URL + @RestController class InboxControllerImpl(private val apService: APService) : InboxController { @Suppress("TooGenericExceptionCaught") - override suspend fun inbox(@RequestBody string: String): ResponseEntity { + override suspend fun inbox( + @RequestBody string: String + ): ResponseEntity { + + val request = (requireNotNull(RequestContextHolder.getRequestAttributes()) as ServletRequestAttributes).request + val parseActivity = try { apService.parseActivity(string) } catch (e: Exception) { @@ -19,7 +31,29 @@ class InboxControllerImpl(private val apService: APService) : InboxController { } LOGGER.info("INBOX Processing Activity Type: {}", parseActivity) try { - apService.processActivity(string, parseActivity) + val url = request.requestURL.toString() + + val headersList = request.headerNames?.toList().orEmpty() + val headers = + headersList.associateWith { header -> request.getHeaders(header)?.toList().orEmpty() } + + val method = when (val method = request.method.lowercase()) { + "get" -> HttpMethod.GET + "post" -> HttpMethod.POST + else -> { + throw IllegalArgumentException("Unsupported method: $method") + } + } + + println(headers) + + apService.processActivity( + string, parseActivity, HttpRequest( + URL(url + request.queryString.orEmpty()), + HttpHeaders(headers), + method + ), headers + ) } catch (e: Exception) { LOGGER.warn("FAILED Process Activity $parseActivity", e) return ResponseEntity(HttpStatus.ACCEPTED) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt index 2db01861..e6e10495 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt @@ -7,6 +7,7 @@ import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubResponse import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.core.external.job.InboxJob import dev.usbharu.hideout.core.service.job.JobQueueParentService +import dev.usbharu.httpsignature.common.HttpRequest import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Qualifier @@ -15,7 +16,12 @@ import org.springframework.stereotype.Service interface APService { fun parseActivity(json: String): ActivityType - suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse? + suspend fun processActivity( + json: String, + type: ActivityType, + httpRequest: HttpRequest, + map: Map> + ): ActivityPubResponse? } enum class ActivityType { @@ -215,11 +221,20 @@ class APServiceImpl( } @Suppress("CyclomaticComplexMethod", "NotImplementedDeclaration") - override suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse { + override suspend fun processActivity( + json: String, + type: ActivityType, + httpRequest: HttpRequest, + map: Map> + ): ActivityPubResponse { logger.debug("process activity: {}", type) jobQueueParentService.schedule(InboxJob) { props[it.json] = json props[it.type] = type.name + val writeValueAsString = objectMapper.writeValueAsString(httpRequest) + println(writeValueAsString) + props[it.httpRequest] = writeValueAsString + props[it.headers] = objectMapper.writeValueAsString(map) } return ActivityPubStringResponse(message = "") } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt index 13d23f57..0020fbd0 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt @@ -12,8 +12,17 @@ import dev.usbharu.hideout.activitypub.service.activity.like.APLikeServiceImpl import dev.usbharu.hideout.activitypub.service.activity.like.ApReactionJobService import dev.usbharu.hideout.activitypub.service.activity.undo.APUndoServiceImpl import dev.usbharu.hideout.activitypub.service.objects.note.ApNoteJobService +import dev.usbharu.hideout.activitypub.service.objects.user.APUserService +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.external.job.* - +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.util.RsaUtil +import dev.usbharu.httpsignature.common.HttpHeaders +import dev.usbharu.httpsignature.common.HttpRequest +import dev.usbharu.httpsignature.common.PublicKey +import dev.usbharu.httpsignature.verify.DefaultSignatureHeaderParser +import dev.usbharu.httpsignature.verify.RsaSha256HttpSignatureVerifier import kjob.core.dsl.JobContextWithProps import kjob.core.job.JobProps import org.slf4j.LoggerFactory @@ -31,7 +40,12 @@ class ApJobServiceImpl( private val APLikeServiceImpl: APLikeServiceImpl, private val APUndoServiceImpl: APUndoServiceImpl, private val APReceiveDeleteServiceImpl: APReceiveDeleteServiceImpl, - @Qualifier("activitypub") private val objectMapper: ObjectMapper + @Qualifier("activitypub") private val objectMapper: ObjectMapper, + private val httpSignatureVerifier: RsaSha256HttpSignatureVerifier, + private val signatureHeaderParser: DefaultSignatureHeaderParser, + private val apUserService: APUserService, + private val userQueryService: UserQueryService, + private val transaction: Transaction ) : ApJobService { @Suppress("REDUNDANT_ELSE_IN_WHEN") override suspend fun processActivity(job: JobContextWithProps, hideoutJob: HideoutJob) { @@ -41,6 +55,29 @@ class ApJobServiceImpl( // Springで作成されるプロキシの都合上パターンマッチングが壊れるので必須 when (hideoutJob) { is InboxJob -> { + val httpRequestString = (job.props as JobProps)[InboxJob.httpRequest] + println(httpRequestString) + val headerString = (job.props as JobProps)[InboxJob.headers] + + val readValue = objectMapper.readValue>>(headerString) + + val httpRequest = + objectMapper.readValue(httpRequestString).copy(headers = HttpHeaders(readValue)) + val signature = signatureHeaderParser.parse(httpRequest.headers) + + val publicKey = transaction.transaction { + try { + userQueryService.findByKeyId(signature.keyId) + } catch (e: FailedToGetResourcesException) { + apUserService.fetchPersonWithEntity(signature.keyId).second + }.publicKey + } + + httpSignatureVerifier.verify( + httpRequest, + PublicKey(RsaUtil.decodeRsaPublicKeyPem(publicKey), signature.keyId) + ) + val typeString = (job.props as JobProps)[InboxJob.type] val json = (job.props as JobProps)[InboxJob.json] val type = ActivityType.valueOf(typeString) @@ -60,6 +97,7 @@ class ApJobServiceImpl( } } } + is ReceiveFollowJob -> { apReceiveFollowJobService.receiveFollowJob( job.props as JobProps diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/HttpSignatureConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/HttpSignatureConfig.kt new file mode 100644 index 00000000..ee3bc409 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/HttpSignatureConfig.kt @@ -0,0 +1,20 @@ +package dev.usbharu.hideout.application.config + +import dev.usbharu.httpsignature.sign.RsaSha256HttpSignatureSigner +import dev.usbharu.httpsignature.verify.DefaultSignatureHeaderParser +import dev.usbharu.httpsignature.verify.RsaSha256HttpSignatureVerifier +import dev.usbharu.httpsignature.verify.SignatureHeaderParser +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class HttpSignatureConfig { + @Bean + fun defaultSignatureHeaderParser(): DefaultSignatureHeaderParser = DefaultSignatureHeaderParser() + + @Bean + fun rsaSha256HttpSignatureVerifier( + signatureHeaderParser: SignatureHeaderParser, + signatureSigner: RsaSha256HttpSignatureSigner + ): RsaSha256HttpSignatureVerifier = RsaSha256HttpSignatureVerifier(signatureHeaderParser, signatureSigner) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt index 94fa81da..238b809f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt @@ -41,4 +41,6 @@ object DeliverRemoveReactionJob : HideoutJob("DeliverRemoveReactionJob") { object InboxJob : HideoutJob("InboxJob") { val json = string("json") val type = string("type") + val httpRequest = string("http_request") + val headers = string("headers") } From cedebb794b98b25e1c79da69e09c83b839ce2a40 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 22 Nov 2023 00:27:44 +0900 Subject: [PATCH 011/116] =?UTF-8?q?refactor:=20ActivityPubStringResponse?= =?UTF-8?q?=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/common/ActivityPubStringResponse.kt | 68 ------------------- .../activity/accept/APAcceptService.kt | 11 ++- .../activity/create/APCreateService.kt | 8 +-- .../activity/delete/APReceiveDeleteService.kt | 3 +- .../delete/APReceiveDeleteServiceImpl.kt | 9 +-- .../activity/follow/APReceiveFollowService.kt | 9 +-- .../service/activity/like/APLikeService.kt | 9 +-- .../service/activity/undo/APUndoService.kt | 15 ++-- .../activitypub/service/common/APService.kt | 8 +-- .../api/inbox/InboxControllerImplTest.kt | 1 - .../accept/APAcceptServiceImplTest.kt | 1 - .../create/APCreateServiceImplTest.kt | 1 - .../activity/like/APLikeServiceImplTest.kt | 1 - .../activity/undo/APUndoServiceImplTest.kt | 1 - 14 files changed, 25 insertions(+), 120 deletions(-) delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/common/ActivityPubStringResponse.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/common/ActivityPubStringResponse.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/common/ActivityPubStringResponse.kt deleted file mode 100644 index 60b58404..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/common/ActivityPubStringResponse.kt +++ /dev/null @@ -1,68 +0,0 @@ -package dev.usbharu.hideout.activitypub.interfaces.api.common - -import dev.usbharu.hideout.activitypub.domain.model.JsonLd -import dev.usbharu.hideout.util.HttpUtil.Activity -import io.ktor.http.* - -sealed class ActivityPubResponse( - val httpStatusCode: HttpStatusCode, - val contentType: ContentType = ContentType.Application.Activity -) { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is ActivityPubResponse) return false - - if (httpStatusCode != other.httpStatusCode) return false - if (contentType != other.contentType) return false - - return true - } - - override fun hashCode(): Int { - var result = httpStatusCode.hashCode() - result = 31 * result + contentType.hashCode() - return result - } - - override fun toString(): String = "ActivityPubResponse(httpStatusCode=$httpStatusCode, contentType=$contentType)" -} - -class ActivityPubStringResponse( - httpStatusCode: HttpStatusCode = HttpStatusCode.OK, - val message: String, - contentType: ContentType = ContentType.Application.Activity -) : ActivityPubResponse(httpStatusCode, contentType) { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is ActivityPubStringResponse) return false - - if (message != other.message) return false - - return true - } - - override fun hashCode(): Int = message.hashCode() - - override fun toString(): String = "ActivityPubStringResponse(message='$message') ${super.toString()}" -} - -class ActivityPubObjectResponse( - httpStatusCode: HttpStatusCode = HttpStatusCode.OK, - val message: JsonLd, - contentType: ContentType = ContentType.Application.Activity -) : ActivityPubResponse(httpStatusCode, contentType) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is ActivityPubObjectResponse) return false - - if (message != other.message) return false - - return true - } - - override fun hashCode(): Int = message.hashCode() - - override fun toString(): String = "ActivityPubObjectResponse(message=$message) ${super.toString()}" -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptService.kt index 72c3f8c3..2e845715 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptService.kt @@ -3,18 +3,15 @@ package dev.usbharu.hideout.activitypub.service.activity.accept import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException import dev.usbharu.hideout.activitypub.domain.model.Accept import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubResponse -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.query.FollowerQueryService import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.user.UserService -import io.ktor.http.* import org.slf4j.LoggerFactory import org.springframework.stereotype.Service interface APAcceptService { - suspend fun receiveAccept(accept: Accept): ActivityPubResponse + suspend fun receiveAccept(accept: Accept) } @Service @@ -24,7 +21,7 @@ class APAcceptServiceImpl( private val followerQueryService: FollowerQueryService, private val transaction: Transaction ) : APAcceptService { - override suspend fun receiveAccept(accept: Accept): ActivityPubResponse { + override suspend fun receiveAccept(accept: Accept) { return transaction.transaction { LOGGER.debug("START Follow") LOGGER.trace("{}", accept) @@ -43,11 +40,11 @@ class APAcceptServiceImpl( if (followerQueryService.alreadyFollow(user.id, follower.id)) { LOGGER.debug("END User already follow from ${follower.url} to ${user.url}") - return@transaction ActivityPubStringResponse(HttpStatusCode.OK, "accepted") + return@transaction } userService.follow(user.id, follower.id) LOGGER.debug("SUCCESS Follow from ${follower.url} to ${user.url}.") - ActivityPubStringResponse(HttpStatusCode.OK, "accepted") + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateService.kt index e0d179ea..afcf4881 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateService.kt @@ -3,16 +3,13 @@ package dev.usbharu.hideout.activitypub.service.activity.create import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException import dev.usbharu.hideout.activitypub.domain.model.Create import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubResponse -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService import dev.usbharu.hideout.application.external.Transaction -import io.ktor.http.* import org.slf4j.LoggerFactory import org.springframework.stereotype.Service interface APCreateService { - suspend fun receiveCreate(create: Create): ActivityPubResponse + suspend fun receiveCreate(create: Create) } @Service @@ -20,7 +17,7 @@ class APCreateServiceImpl( private val apNoteService: APNoteService, private val transaction: Transaction ) : APCreateService { - override suspend fun receiveCreate(create: Create): ActivityPubResponse { + override suspend fun receiveCreate(create: Create) { LOGGER.debug("START Create new remote note.") LOGGER.trace("{}", create) @@ -34,7 +31,6 @@ class APCreateServiceImpl( val note = value as Note apNoteService.fetchNote(note) LOGGER.debug("SUCCESS Create new remote note. ${note.id} by ${note.attributedTo}") - ActivityPubStringResponse(HttpStatusCode.OK, "Created") } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteService.kt index 9d047605..68505f22 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteService.kt @@ -1,8 +1,7 @@ package dev.usbharu.hideout.activitypub.service.activity.delete import dev.usbharu.hideout.activitypub.domain.model.Delete -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubResponse interface APReceiveDeleteService { - suspend fun receiveDelete(delete: Delete): ActivityPubResponse + suspend fun receiveDelete(delete: Delete) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteServiceImpl.kt index c00aeda6..e75fa90c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteServiceImpl.kt @@ -2,13 +2,10 @@ package dev.usbharu.hideout.activitypub.service.activity.delete import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException import dev.usbharu.hideout.activitypub.domain.model.Delete -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubResponse -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.query.PostQueryService -import io.ktor.http.* import org.springframework.stereotype.Service @Service @@ -17,15 +14,15 @@ class APReceiveDeleteServiceImpl( private val postRepository: PostRepository, private val transaction: Transaction ) : APReceiveDeleteService { - override suspend fun receiveDelete(delete: Delete): ActivityPubResponse = transaction.transaction { + override suspend fun receiveDelete(delete: Delete) = transaction.transaction { val deleteId = delete.`object`?.id ?: throw IllegalActivityPubObjectException("object.id is null") val post = try { postQueryService.findByApId(deleteId) } catch (_: FailedToGetResourcesException) { - return@transaction ActivityPubStringResponse(HttpStatusCode.OK, "Resource not found or already deleted") + return@transaction } postRepository.delete(post.id) - return@transaction ActivityPubStringResponse(HttpStatusCode.OK, "Resource was deleted.") + return@transaction } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt index 95b47ad1..2011fce1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt @@ -2,17 +2,14 @@ package dev.usbharu.hideout.activitypub.service.activity.follow import com.fasterxml.jackson.databind.ObjectMapper import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubResponse -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.core.external.job.ReceiveFollowJob import dev.usbharu.hideout.core.service.job.JobQueueParentService -import io.ktor.http.* import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Service interface APReceiveFollowService { - suspend fun receiveFollow(follow: Follow): ActivityPubResponse + suspend fun receiveFollow(follow: Follow) } @Service @@ -20,14 +17,14 @@ class APReceiveFollowServiceImpl( private val jobQueueParentService: JobQueueParentService, @Qualifier("activitypub") private val objectMapper: ObjectMapper ) : APReceiveFollowService { - override suspend fun receiveFollow(follow: Follow): ActivityPubResponse { + override suspend fun receiveFollow(follow: Follow) { logger.info("FOLLOW from: {} to: {}", follow.actor, follow.`object`) jobQueueParentService.schedule(ReceiveFollowJob) { props[ReceiveFollowJob.actor] = follow.actor props[ReceiveFollowJob.follow] = objectMapper.writeValueAsString(follow) props[ReceiveFollowJob.targetActor] = follow.`object` } - return ActivityPubStringResponse(HttpStatusCode.OK, "{}", ContentType.Application.Json) + return } companion object { diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeService.kt index c37af164..5b59550e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeService.kt @@ -3,19 +3,16 @@ package dev.usbharu.hideout.activitypub.service.activity.like import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubResponse -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.query.PostQueryService import dev.usbharu.hideout.core.service.reaction.ReactionService -import io.ktor.http.* import org.slf4j.LoggerFactory import org.springframework.stereotype.Service interface APLikeService { - suspend fun receiveLike(like: Like): ActivityPubResponse + suspend fun receiveLike(like: Like) } @Service @@ -26,7 +23,7 @@ class APLikeServiceImpl( private val postQueryService: PostQueryService, private val transaction: Transaction ) : APLikeService { - override suspend fun receiveLike(like: Like): ActivityPubResponse { + override suspend fun receiveLike(like: Like) { LOGGER.debug("START Add Like") LOGGER.trace("{}", like) @@ -57,7 +54,7 @@ class APLikeServiceImpl( ) LOGGER.debug("SUCCESS Add Like($content) from ${person.second.url} to ${post.url}") } - return ActivityPubStringResponse(HttpStatusCode.OK, "") + return } companion object { diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoService.kt index 372ff33c..e0348b6c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoService.kt @@ -2,17 +2,14 @@ package dev.usbharu.hideout.activitypub.service.activity.undo import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.activitypub.domain.model.Undo -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubResponse -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.user.UserService -import io.ktor.http.* import org.springframework.stereotype.Service interface APUndoService { - suspend fun receiveUndo(undo: Undo): ActivityPubResponse + suspend fun receiveUndo(undo: Undo) } @Service @@ -23,22 +20,22 @@ class APUndoServiceImpl( private val userQueryService: UserQueryService, private val transaction: Transaction ) : APUndoService { - override suspend fun receiveUndo(undo: Undo): ActivityPubResponse { + override suspend fun receiveUndo(undo: Undo) { if (undo.actor == null) { - return ActivityPubStringResponse(HttpStatusCode.BadRequest, "actor is null") + return } val type = undo.`object`?.type.orEmpty() .firstOrNull { it == "Block" || it == "Follow" || it == "Like" || it == "Announce" || it == "Accept" } - ?: return ActivityPubStringResponse(HttpStatusCode.BadRequest, "unknown type ${undo.`object`?.type}") + ?: return when (type) { "Follow" -> { val follow = undo.`object` as Follow if (follow.`object` == null) { - return ActivityPubStringResponse(HttpStatusCode.BadRequest, "object.object is null") + return } transaction.transaction { apUserService.fetchPerson(undo.actor!!, follow.`object`) @@ -46,7 +43,7 @@ class APUndoServiceImpl( val target = userQueryService.findByUrl(follow.`object`!!) userService.unfollow(target.id, follower.id) } - return ActivityPubStringResponse(HttpStatusCode.OK, "Accept") + return } else -> {} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt index e6e10495..e23ed018 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt @@ -3,8 +3,6 @@ package dev.usbharu.hideout.activitypub.service.common import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import dev.usbharu.hideout.activitypub.domain.exception.JsonParseException -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubResponse -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.core.external.job.InboxJob import dev.usbharu.hideout.core.service.job.JobQueueParentService import dev.usbharu.httpsignature.common.HttpRequest @@ -21,7 +19,7 @@ interface APService { type: ActivityType, httpRequest: HttpRequest, map: Map> - ): ActivityPubResponse? + ) } enum class ActivityType { @@ -226,7 +224,7 @@ class APServiceImpl( type: ActivityType, httpRequest: HttpRequest, map: Map> - ): ActivityPubResponse { + ) { logger.debug("process activity: {}", type) jobQueueParentService.schedule(InboxJob) { props[it.json] = json @@ -236,6 +234,6 @@ class APServiceImpl( props[it.httpRequest] = writeValueAsString props[it.headers] = objectMapper.writeValueAsString(map) } - return ActivityPubStringResponse(message = "") + return } } diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt index a91e5b7f..1f2ee0a0 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt @@ -1,7 +1,6 @@ package dev.usbharu.hideout.activitypub.interfaces.api.inbox import dev.usbharu.hideout.activitypub.domain.exception.JsonParseException -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.activitypub.service.common.APService import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptServiceImplTest.kt index 77b3f74d..8f5ab942 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptServiceImplTest.kt @@ -4,7 +4,6 @@ import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObject import dev.usbharu.hideout.activitypub.domain.model.Accept import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.core.query.FollowerQueryService import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.user.UserService diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateServiceImplTest.kt index 87e66044..5eaff691 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateServiceImplTest.kt @@ -4,7 +4,6 @@ import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObject import dev.usbharu.hideout.activitypub.domain.model.Create import dev.usbharu.hideout.activitypub.domain.model.Like import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService import io.ktor.http.* import kotlinx.coroutines.test.runTest diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeServiceImplTest.kt index 3b37fffc..0cb421f5 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeServiceImplTest.kt @@ -4,7 +4,6 @@ import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubRe import dev.usbharu.hideout.activitypub.domain.model.Like import dev.usbharu.hideout.activitypub.domain.model.Note import dev.usbharu.hideout.activitypub.domain.model.Person -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.core.query.PostQueryService diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoServiceImplTest.kt index fb187aec..e465944e 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoServiceImplTest.kt @@ -2,7 +2,6 @@ package dev.usbharu.hideout.activitypub.service.activity.undo import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.activitypub.domain.model.Undo -import dev.usbharu.hideout.activitypub.interfaces.api.common.ActivityPubStringResponse import dev.usbharu.hideout.core.query.UserQueryService import io.ktor.http.* import kotlinx.coroutines.test.runTest From 2dbbed9a5a7206b762b5484801dea91db6dd3041 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 22 Nov 2023 00:56:12 +0900 Subject: [PATCH 012/116] =?UTF-8?q?feat:=20ActivityPub=E3=81=AE=E5=87=A6?= =?UTF-8?q?=E7=90=86=E5=85=B1=E9=80=9Ainterface=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ActivityPubProcessException.kt | 21 +++++++++++++++ .../exception/FailedProcessException.kt | 14 ++++++++++ .../tmp/AbstractActivityPubProcessor.kt | 27 +++++++++++++++++++ .../service/tmp/ActivityPubProcessor.kt | 10 +++++++ 4 files changed, 72 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/ActivityPubProcessException.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessor.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/ActivityPubProcessException.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/ActivityPubProcessException.kt new file mode 100644 index 00000000..c8cf6a0c --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/ActivityPubProcessException.kt @@ -0,0 +1,21 @@ +package dev.usbharu.hideout.activitypub.domain.exception + +import java.io.Serial + +class ActivityPubProcessException : RuntimeException { + constructor() : super() + constructor(message: String?) : super(message) + constructor(message: String?, cause: Throwable?) : super(message, cause) + constructor(cause: Throwable?) : super(cause) + constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( + message, + cause, + enableSuppression, + writableStackTrace + ) + + companion object { + @Serial + private const val serialVersionUID: Long = 5370068873167636639L + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt new file mode 100644 index 00000000..31c4b47e --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt @@ -0,0 +1,14 @@ +package dev.usbharu.hideout.activitypub.domain.exception + +class FailedProcessException : RuntimeException { + constructor() : super() + constructor(message: String?) : super(message) + constructor(message: String?, cause: Throwable?) : super(message, cause) + constructor(cause: Throwable?) : super(cause) + constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( + message, + cause, + enableSuppression, + writableStackTrace + ) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt new file mode 100644 index 00000000..ff23a65e --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt @@ -0,0 +1,27 @@ +package dev.usbharu.hideout.activitypub.service.tmp + +import dev.usbharu.hideout.activitypub.domain.exception.ActivityPubProcessException +import dev.usbharu.hideout.activitypub.domain.exception.FailedProcessException +import dev.usbharu.hideout.activitypub.domain.model.objects.Object +import dev.usbharu.hideout.application.external.Transaction +import org.slf4j.LoggerFactory + +abstract class AbstractActivityPubProcessor(val transaction: Transaction) : ActivityPubProcessor { + private val logger = LoggerFactory.getLogger(this::class.java) + + override suspend fun process(activity: T) { + logger.info("START ActivityPub process") + try { + transaction.transaction { + internalProcess(activity) + } + } catch (e: ActivityPubProcessException) { + logger.warn("FAILED ActivityPub process", e) + throw FailedProcessException("Failed process", e) + } + logger.info("SUCCESS ActivityPub process") + } + + abstract suspend fun internalProcess(activity: T) + +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessor.kt new file mode 100644 index 00000000..919bf836 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessor.kt @@ -0,0 +1,10 @@ +package dev.usbharu.hideout.activitypub.service.tmp + +import dev.usbharu.hideout.activitypub.domain.model.objects.Object +import dev.usbharu.hideout.activitypub.service.common.ActivityType + +interface ActivityPubProcessor { + suspend fun process(activity: T) + + fun isSupported(activityType: ActivityType): Boolean +} From c4c9b4872220ad40f8dc1f8929be4f976cf06f72 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 22 Nov 2023 01:57:42 +0900 Subject: [PATCH 013/116] =?UTF-8?q?feat:=20InboxJobProcessor=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tmp/AbstractActivityPubProcessor.kt | 4 +- .../service/tmp/ActivityPubProcessContext.kt | 14 +++ .../service/tmp/ActivityPubProcessor.kt | 4 +- .../service/tmp/InboxJobProcessor.kt | 94 +++++++++++++++++++ 4 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessContext.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt index ff23a65e..6a3aba7e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt @@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory abstract class AbstractActivityPubProcessor(val transaction: Transaction) : ActivityPubProcessor { private val logger = LoggerFactory.getLogger(this::class.java) - override suspend fun process(activity: T) { + override suspend fun process(activity: ActivityPubProcessContext) { logger.info("START ActivityPub process") try { transaction.transaction { @@ -22,6 +22,6 @@ abstract class AbstractActivityPubProcessor(val transaction: Transac logger.info("SUCCESS ActivityPub process") } - abstract suspend fun internalProcess(activity: T) + abstract suspend fun internalProcess(activity: ActivityPubProcessContext) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessContext.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessContext.kt new file mode 100644 index 00000000..6f45fd20 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessContext.kt @@ -0,0 +1,14 @@ +package dev.usbharu.hideout.activitypub.service.tmp + +import com.fasterxml.jackson.databind.JsonNode +import dev.usbharu.hideout.activitypub.domain.model.objects.Object +import dev.usbharu.httpsignature.common.HttpRequest +import dev.usbharu.httpsignature.verify.Signature + +data class ActivityPubProcessContext( + val activity: T, + val jsonNode: JsonNode, + val httpRequest: HttpRequest, + val signature: Signature?, + val isAuthorized: Boolean +) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessor.kt index 919bf836..350f1aea 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessor.kt @@ -4,7 +4,9 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.service.common.ActivityType interface ActivityPubProcessor { - suspend fun process(activity: T) + suspend fun process(activity: ActivityPubProcessContext) fun isSupported(activityType: ActivityType): Boolean + + fun type(): Class } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt new file mode 100644 index 00000000..1489765c --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt @@ -0,0 +1,94 @@ +package dev.usbharu.hideout.activitypub.service.tmp + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.model.objects.Object +import dev.usbharu.hideout.activitypub.service.common.ActivityType +import dev.usbharu.hideout.activitypub.service.objects.user.APUserService +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.external.job.InboxJob +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.util.RsaUtil +import dev.usbharu.httpsignature.common.HttpHeaders +import dev.usbharu.httpsignature.common.HttpRequest +import dev.usbharu.httpsignature.common.PublicKey +import dev.usbharu.httpsignature.verify.HttpSignatureVerifier +import dev.usbharu.httpsignature.verify.Signature +import dev.usbharu.httpsignature.verify.SignatureHeaderParser +import kjob.core.job.JobProps +import org.slf4j.LoggerFactory + +class InboxJobProcessor( + private val activityPubProcessorList: List>, + private val objectMapper: ObjectMapper, + private val signatureHeaderParser: SignatureHeaderParser, + private val signatureVerifier: HttpSignatureVerifier, + private val userQueryService: UserQueryService, + private val apUserService: APUserService +) { + suspend fun process(props: JobProps) { + + val type = ActivityType.valueOf(props[InboxJob.type]) + val jsonString = objectMapper.readTree(props[InboxJob.json]) + val httpRequestString = props[InboxJob.httpRequest] + val headersString = props[InboxJob.headers] + + logger.info("START Process inbox. type: {}", type) + logger.trace("type: {} \njson: \n{}", type, jsonString.toPrettyString()) + + val map = objectMapper.readValue>>(headersString) + + val httpRequest = + objectMapper.readValue(httpRequestString).copy(headers = HttpHeaders(map)) + + logger.trace("request: {}\nheaders: {}", httpRequest, map) + + val signature = parseSignatureHeader(httpRequest.headers) + + logger.debug("Has signature? {}", signature != null) + + val verify = signature?.let { verifyHttpSignature(httpRequest, it) } ?: false + + logger.debug("Is verifying success? {}", verify) + + val activityPubProcessor = activityPubProcessorList.firstOrNull { it.isSupported(type) } + + if (activityPubProcessor == null) { + logger.warn("ActivityType {} is not support.", type) + throw IllegalStateException("ActivityPubProcessor not found.") + } + + val value = objectMapper.treeToValue(jsonString, activityPubProcessor.type()) + activityPubProcessor.process(ActivityPubProcessContext(value, jsonString, httpRequest, signature, verify)) + + logger.info("SUCCESS Process inbox. type: {}", type) + } + + private suspend fun verifyHttpSignature(httpRequest: HttpRequest, signature: Signature): Boolean { + val user = try { + userQueryService.findByKeyId(signature.keyId) + } catch (_: FailedToGetResourcesException) { + apUserService.fetchPersonWithEntity(signature.keyId).second + } + + val verify = signatureVerifier.verify( + httpRequest, + PublicKey(RsaUtil.decodeRsaPublicKeyPem(user.publicKey), signature.keyId) + ) + + return verify.success + } + + private fun parseSignatureHeader(httpHeaders: HttpHeaders): Signature? { + return try { + signatureHeaderParser.parse(httpHeaders) + } catch (e: RuntimeException) { + logger.trace("FAILED parse signature header", e) + null + } + } + + companion object { + private val logger = LoggerFactory.getLogger(InboxJobProcessor::class.java) + } +} From 89c299d3c0270d2f2eba39efa3226b82368d9fe5 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 22 Nov 2023 02:00:19 +0900 Subject: [PATCH 014/116] =?UTF-8?q?feat:=20InboxJobProcessor=E3=82=92?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E3=81=99=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 --- .../service/common/ApJobServiceImpl.kt | 73 +------------------ .../service/tmp/InboxJobProcessor.kt | 2 + 2 files changed, 5 insertions(+), 70 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt index 0020fbd0..5da0356c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt @@ -1,28 +1,11 @@ package dev.usbharu.hideout.activitypub.service.common import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.service.activity.accept.APAcceptServiceImpl -import dev.usbharu.hideout.activitypub.service.activity.create.APCreateServiceImpl -import dev.usbharu.hideout.activitypub.service.activity.delete.APReceiveDeleteServiceImpl import dev.usbharu.hideout.activitypub.service.activity.follow.APReceiveFollowJobService -import dev.usbharu.hideout.activitypub.service.activity.follow.APReceiveFollowServiceImpl -import dev.usbharu.hideout.activitypub.service.activity.like.APLikeServiceImpl import dev.usbharu.hideout.activitypub.service.activity.like.ApReactionJobService -import dev.usbharu.hideout.activitypub.service.activity.undo.APUndoServiceImpl import dev.usbharu.hideout.activitypub.service.objects.note.ApNoteJobService -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.activitypub.service.tmp.InboxJobProcessor import dev.usbharu.hideout.core.external.job.* -import dev.usbharu.hideout.core.query.UserQueryService -import dev.usbharu.hideout.util.RsaUtil -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.httpsignature.common.PublicKey -import dev.usbharu.httpsignature.verify.DefaultSignatureHeaderParser -import dev.usbharu.httpsignature.verify.RsaSha256HttpSignatureVerifier import kjob.core.dsl.JobContextWithProps import kjob.core.job.JobProps import org.slf4j.LoggerFactory @@ -34,18 +17,8 @@ class ApJobServiceImpl( private val apReceiveFollowJobService: APReceiveFollowJobService, private val apNoteJobService: ApNoteJobService, private val apReactionJobService: ApReactionJobService, - private val APAcceptServiceImpl: APAcceptServiceImpl, - private val APReceiveFollowServiceImpl: APReceiveFollowServiceImpl, - private val APCreateServiceImpl: APCreateServiceImpl, - private val APLikeServiceImpl: APLikeServiceImpl, - private val APUndoServiceImpl: APUndoServiceImpl, - private val APReceiveDeleteServiceImpl: APReceiveDeleteServiceImpl, @Qualifier("activitypub") private val objectMapper: ObjectMapper, - private val httpSignatureVerifier: RsaSha256HttpSignatureVerifier, - private val signatureHeaderParser: DefaultSignatureHeaderParser, - private val apUserService: APUserService, - private val userQueryService: UserQueryService, - private val transaction: Transaction + private val inboxJobProcessor: InboxJobProcessor ) : ApJobService { @Suppress("REDUNDANT_ELSE_IN_WHEN") override suspend fun processActivity(job: JobContextWithProps, hideoutJob: HideoutJob) { @@ -55,47 +28,7 @@ class ApJobServiceImpl( // Springで作成されるプロキシの都合上パターンマッチングが壊れるので必須 when (hideoutJob) { is InboxJob -> { - val httpRequestString = (job.props as JobProps)[InboxJob.httpRequest] - println(httpRequestString) - val headerString = (job.props as JobProps)[InboxJob.headers] - - val readValue = objectMapper.readValue>>(headerString) - - val httpRequest = - objectMapper.readValue(httpRequestString).copy(headers = HttpHeaders(readValue)) - val signature = signatureHeaderParser.parse(httpRequest.headers) - - val publicKey = transaction.transaction { - try { - userQueryService.findByKeyId(signature.keyId) - } catch (e: FailedToGetResourcesException) { - apUserService.fetchPersonWithEntity(signature.keyId).second - }.publicKey - } - - httpSignatureVerifier.verify( - httpRequest, - PublicKey(RsaUtil.decodeRsaPublicKeyPem(publicKey), signature.keyId) - ) - - val typeString = (job.props as JobProps)[InboxJob.type] - val json = (job.props as JobProps)[InboxJob.json] - val type = ActivityType.valueOf(typeString) - when (type) { - ActivityType.Accept -> APAcceptServiceImpl.receiveAccept(objectMapper.readValue(json)) - ActivityType.Follow -> - APReceiveFollowServiceImpl - .receiveFollow(objectMapper.readValue(json, Follow::class.java)) - - ActivityType.Create -> APCreateServiceImpl.receiveCreate(objectMapper.readValue(json)) - ActivityType.Like -> APLikeServiceImpl.receiveLike(objectMapper.readValue(json)) - ActivityType.Undo -> APUndoServiceImpl.receiveUndo(objectMapper.readValue(json)) - ActivityType.Delete -> APReceiveDeleteServiceImpl.receiveDelete(objectMapper.readValue(json)) - - else -> { - throw IllegalArgumentException("$type is not supported.") - } - } + inboxJobProcessor.process(job.props as JobProps) } is ReceiveFollowJob -> { diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt index 1489765c..99ad3fb9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt @@ -17,7 +17,9 @@ import dev.usbharu.httpsignature.verify.Signature import dev.usbharu.httpsignature.verify.SignatureHeaderParser import kjob.core.job.JobProps import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +@Service class InboxJobProcessor( private val activityPubProcessorList: List>, private val objectMapper: ObjectMapper, From 3009ca153290a5c9d8e8f26f7ed73cf77e6bfa68 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 22 Nov 2023 02:14:21 +0900 Subject: [PATCH 015/116] =?UTF-8?q?feat:=20Create=E3=81=AE=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=83=86=E3=82=A3=E3=83=93=E3=83=86=E3=82=A3=E3=81=AB?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HttpSignatureUnauthorizedException.kt | 14 ++++++++++++ .../tmp/AbstractActivityPubProcessor.kt | 9 +++++++- .../tmp/impl/CreateActivityProcessor.kt | 22 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/impl/CreateActivityProcessor.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt new file mode 100644 index 00000000..9abccec6 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt @@ -0,0 +1,14 @@ +package dev.usbharu.hideout.activitypub.domain.exception + +class HttpSignatureUnauthorizedException : RuntimeException { + constructor() : super() + constructor(message: String?) : super(message) + constructor(message: String?, cause: Throwable?) : super(message, cause) + constructor(cause: Throwable?) : super(cause) + constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( + message, + cause, + enableSuppression, + writableStackTrace + ) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt index 6a3aba7e..c5d927c9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt @@ -2,14 +2,21 @@ package dev.usbharu.hideout.activitypub.service.tmp import dev.usbharu.hideout.activitypub.domain.exception.ActivityPubProcessException import dev.usbharu.hideout.activitypub.domain.exception.FailedProcessException +import dev.usbharu.hideout.activitypub.domain.exception.HttpSignatureUnauthorizedException import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.application.external.Transaction import org.slf4j.LoggerFactory -abstract class AbstractActivityPubProcessor(val transaction: Transaction) : ActivityPubProcessor { +abstract class AbstractActivityPubProcessor( + private val transaction: Transaction, + private val allowUnauthorized: Boolean = false +) : ActivityPubProcessor { private val logger = LoggerFactory.getLogger(this::class.java) override suspend fun process(activity: ActivityPubProcessContext) { + if (activity.isAuthorized.not() && allowUnauthorized.not()) { + throw HttpSignatureUnauthorizedException() + } logger.info("START ActivityPub process") try { transaction.transaction { diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/impl/CreateActivityProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/impl/CreateActivityProcessor.kt new file mode 100644 index 00000000..60918d01 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/impl/CreateActivityProcessor.kt @@ -0,0 +1,22 @@ +package dev.usbharu.hideout.activitypub.service.tmp.impl + +import dev.usbharu.hideout.activitypub.domain.model.Create +import dev.usbharu.hideout.activitypub.domain.model.Note +import dev.usbharu.hideout.activitypub.service.common.ActivityType +import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService +import dev.usbharu.hideout.activitypub.service.tmp.AbstractActivityPubProcessor +import dev.usbharu.hideout.activitypub.service.tmp.ActivityPubProcessContext +import dev.usbharu.hideout.application.external.Transaction +import org.springframework.stereotype.Service + +@Service +class CreateActivityProcessor(transaction: Transaction, private val apNoteService: APNoteService) : + AbstractActivityPubProcessor(transaction, false) { + override suspend fun internalProcess(activity: ActivityPubProcessContext) { + apNoteService.fetchNote(activity.activity.`object` as Note) + } + + override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Create + + override fun type(): Class = Create::class.java +} From ea9a999ae96270b8335c83658a532940796fc0b0 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 25 Nov 2023 16:23:00 +0900 Subject: [PATCH 016/116] =?UTF-8?q?feat:=20=E5=9E=8B=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E3=82=B8=E3=83=A7=E3=83=96=E3=82=AD=E3=83=A5=E3=83=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/core/external/job/HideoutJob.kt | 30 +++++++++++++++---- .../kjobexposed/KJobJobQueueParentService.kt | 6 ++++ .../core/service/job/JobQueueParentService.kt | 2 ++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt index 238b809f..9c4687d6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt @@ -2,26 +2,44 @@ package dev.usbharu.hideout.core.external.job import kjob.core.Job import kjob.core.Prop +import kjob.core.dsl.ScheduleContext +import kjob.core.job.JobProps import org.springframework.stereotype.Component -sealed class HideoutJob(name: String = "") : Job(name) +abstract class HideoutJob>(name: String = "") : Job(name) { + abstract fun convert(value: T): ScheduleContext.(R) -> Unit + abstract fun convert(props: JobProps): T +} @Component -object ReceiveFollowJob : HideoutJob("ReceiveFollowJob") { +object ReceiveFollowJob : HideoutJob("ReceiveFollowJob") { val actor: Prop = string("actor") val follow: Prop = string("follow") val targetActor: Prop = string("targetActor") + + override fun convert(value: String): ScheduleContext.(ReceiveFollowJob) -> Unit = { + props[it.follow] = value + } + + override fun convert(props: JobProps): String = TODO("Not yet implemented") } @Component -object DeliverPostJob : HideoutJob("DeliverPostJob") { +object DeliverPostJob : HideoutJob("DeliverPostJob") { val create = string("create") val inbox = string("inbox") val actor = string("actor") + override fun convert(value: String): ScheduleContext.(DeliverPostJob) -> Unit { + TODO("Not yet implemented") + } + + override fun convert(props: JobProps): String { + TODO("Not yet implemented") + } } @Component -object DeliverReactionJob : HideoutJob("DeliverReactionJob") { +object DeliverReactionJob : HideoutJob("DeliverReactionJob") { val reaction: Prop = string("reaction") val postUrl: Prop = string("postUrl") val actor: Prop = string("actor") @@ -30,7 +48,7 @@ object DeliverReactionJob : HideoutJob("DeliverReactionJob") { } @Component -object DeliverRemoveReactionJob : HideoutJob("DeliverRemoveReactionJob") { +object DeliverRemoveReactionJob : HideoutJob("DeliverRemoveReactionJob") { val id: Prop = string("id") val inbox: Prop = string("inbox") val actor: Prop = string("actor") @@ -38,7 +56,7 @@ object DeliverRemoveReactionJob : HideoutJob("DeliverRemoveReactionJob") { } @Component -object InboxJob : HideoutJob("InboxJob") { +object InboxJob : HideoutJob("InboxJob") { val json = string("json") val type = string("type") val httpRequest = string("http_request") diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueParentService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueParentService.kt index 0e02b011..5bc3ec89 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueParentService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueParentService.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.core.infrastructure.kjobexposed +import dev.usbharu.hideout.core.external.job.HideoutJob import dev.usbharu.hideout.core.service.job.JobQueueParentService import kjob.core.Job import kjob.core.KJob @@ -29,4 +30,9 @@ class KJobJobQueueParentService() : JobQueueParentService { logger.debug("schedule job={}", job.name) kjob.schedule(job, block) } + + override suspend fun > scheduleTypeSafe(job: J, jobProps: T) { + val convert: ScheduleContext.(J) -> Unit = job.convert(jobProps) + kjob.schedule(job, convert) + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobQueueParentService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobQueueParentService.kt index 38f8111f..eee6d660 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobQueueParentService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobQueueParentService.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.core.service.job +import dev.usbharu.hideout.core.external.job.HideoutJob import kjob.core.Job import kjob.core.dsl.ScheduleContext import org.springframework.stereotype.Service @@ -9,4 +10,5 @@ interface JobQueueParentService { fun init(jobDefines: List) suspend fun schedule(job: J, block: ScheduleContext.(J) -> Unit = {}) + suspend fun > scheduleTypeSafe(job: J, jobProps: T) } From dcac609b9432837fe38270301364d6e3e5345069 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 25 Nov 2023 16:46:03 +0900 Subject: [PATCH 017/116] =?UTF-8?q?feat:=20=E5=9E=8B=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E3=82=B8=E3=83=A7=E3=83=96=E3=82=AD=E3=83=A5=E3=83=BC=E3=82=92?= =?UTF-8?q?=E3=81=99=E3=81=B9=E3=81=A6=E3=81=AE=E3=82=B8=E3=83=A7=E3=83=96?= =?UTF-8?q?=E3=82=AD=E3=83=A5=E3=83=BC=E3=81=AB=E9=81=A9=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/core/external/job/HideoutJob.kt | 118 ++++++++++++++++-- .../core/service/job/JobQueueParentService.kt | 2 + 2 files changed, 107 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt index 9c4687d6..30c02371 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt @@ -11,54 +11,146 @@ abstract class HideoutJob>(name: String = "") : Job(name abstract fun convert(props: JobProps): T } +data class ReceiveFollowJobParam( + val actor: String, + val follow: String, + val targetActor: String +) + @Component -object ReceiveFollowJob : HideoutJob("ReceiveFollowJob") { +object ReceiveFollowJob : HideoutJob("ReceiveFollowJob") { val actor: Prop = string("actor") val follow: Prop = string("follow") val targetActor: Prop = string("targetActor") - override fun convert(value: String): ScheduleContext.(ReceiveFollowJob) -> Unit = { - props[it.follow] = value + override fun convert(value: ReceiveFollowJobParam): ScheduleContext.(ReceiveFollowJob) -> Unit = { + props[follow] = value.follow + props[actor] = value.actor + props[targetActor] = value.targetActor + } - override fun convert(props: JobProps): String = TODO("Not yet implemented") + override fun convert(props: JobProps): ReceiveFollowJobParam = ReceiveFollowJobParam( + actor = props[actor], + follow = props[follow], + targetActor = props[targetActor] + ) } +data class DeliverPostJobParam( + val create: String, + val inbox: String, + val actor: String +) + @Component -object DeliverPostJob : HideoutJob("DeliverPostJob") { +object DeliverPostJob : HideoutJob("DeliverPostJob") { val create = string("create") val inbox = string("inbox") val actor = string("actor") - override fun convert(value: String): ScheduleContext.(DeliverPostJob) -> Unit { - TODO("Not yet implemented") + override fun convert(value: DeliverPostJobParam): ScheduleContext.(DeliverPostJob) -> Unit = { + props[create] = value.create + props[inbox] = value.inbox + props[actor] = value.actor } - override fun convert(props: JobProps): String { - TODO("Not yet implemented") - } + override fun convert(props: JobProps): DeliverPostJobParam = DeliverPostJobParam( + create = props[create], + inbox = props[inbox], + actor = props[actor] + ) } +data class DeliverReactionJobParam( + val reaction: String, + val postUrl: String, + val actor: String, + val inbox: String, + val id: String +) + @Component -object DeliverReactionJob : HideoutJob("DeliverReactionJob") { +object DeliverReactionJob : HideoutJob("DeliverReactionJob") { val reaction: Prop = string("reaction") val postUrl: Prop = string("postUrl") val actor: Prop = string("actor") val inbox: Prop = string("inbox") val id: Prop = string("id") + override fun convert(value: DeliverReactionJobParam): ScheduleContext.(DeliverReactionJob) -> Unit = + { + props[reaction] = value.reaction + props[postUrl] = value.postUrl + props[actor] = value.actor + props[inbox] = value.inbox + props[id] = value.id + } + + override fun convert(props: JobProps): DeliverReactionJobParam = DeliverReactionJobParam( + props[reaction], + props[postUrl], + props[actor], + props[inbox], + props[id] + ) } +data class DeliverRemoveReactionJobParam( + val id: String, + val inbox: String, + val actor: String, + val like: String +) + @Component -object DeliverRemoveReactionJob : HideoutJob("DeliverRemoveReactionJob") { +object DeliverRemoveReactionJob : + HideoutJob("DeliverRemoveReactionJob") { val id: Prop = string("id") val inbox: Prop = string("inbox") val actor: Prop = string("actor") val like: Prop = string("like") + + override fun convert(value: DeliverRemoveReactionJobParam): ScheduleContext.(DeliverRemoveReactionJob) -> Unit = + { + props[id] = value.id + props[inbox] = value.inbox + props[actor] = value.actor + props[like] = value.like + } + + override fun convert(props: JobProps): DeliverRemoveReactionJobParam = + DeliverRemoveReactionJobParam( + id = props[id], + inbox = props[inbox], + actor = props[actor], + like = props[like] + ) } +data class InboxJobParam( + val json: String, + val type: String, + val httpRequest: String, + val headers: String +) + @Component -object InboxJob : HideoutJob("InboxJob") { +object InboxJob : HideoutJob("InboxJob") { val json = string("json") val type = string("type") val httpRequest = string("http_request") val headers = string("headers") + + override fun convert(value: InboxJobParam): ScheduleContext.(InboxJob) -> Unit = { + props[json] = value.json + props[type] = value.type + props[httpRequest] = value.httpRequest + props[headers] = value.headers + } + + override fun convert(props: JobProps): InboxJobParam = InboxJobParam( + props[json], + props[type], + props[httpRequest], + props[headers] + ) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobQueueParentService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobQueueParentService.kt index eee6d660..acec3f4a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobQueueParentService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobQueueParentService.kt @@ -9,6 +9,8 @@ import org.springframework.stereotype.Service interface JobQueueParentService { fun init(jobDefines: List) + + @Deprecated("use type safe → scheduleTypeSafe") suspend fun schedule(job: J, block: ScheduleContext.(J) -> Unit = {}) suspend fun > scheduleTypeSafe(job: J, jobProps: T) } From 1e8f49b5544117ce92159cfa97f190cc9a7a2de5 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 26 Nov 2023 11:19:25 +0900 Subject: [PATCH 018/116] =?UTF-8?q?feat:=20=E5=9E=8B=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E3=81=AA=E3=82=B8=E3=83=A7=E3=83=96=E3=82=AD=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E3=81=AE=E3=83=97=E3=83=AD=E3=82=BB=E3=83=83=E3=82=B5=E3=83=BC?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kjobexposed/KJobJobQueueWorkerService.kt | 8 +++----- .../hideout/core/service/job/JobProcessorService.kt | 8 ++++++++ .../hideout/core/service/job/JobQueueWorkerService.kt | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessorService.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt index 98c3a488..8404a2db 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt @@ -1,14 +1,14 @@ package dev.usbharu.hideout.core.infrastructure.kjobexposed +import dev.usbharu.hideout.core.external.job.HideoutJob import dev.usbharu.hideout.core.service.job.JobQueueWorkerService +import kjob.core.dsl.JobContextWithProps import kjob.core.dsl.JobRegisterContext import kjob.core.dsl.KJobFunctions import kjob.core.kjob import org.jetbrains.exposed.sql.transactions.TransactionManager import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.stereotype.Service -import dev.usbharu.hideout.core.external.job.HideoutJob as HJ -import kjob.core.dsl.JobContextWithProps as JCWP @Service @ConditionalOnProperty(name = ["hideout.use-mongodb"], havingValue = "false", matchIfMissing = true) @@ -23,9 +23,7 @@ class KJobJobQueueWorkerService() : JobQueueWorkerService { }.start() } - override fun init( - defines: List>.(HJ) -> KJobFunctions>>> - ) { + override fun > init(defines: List>.(R) -> KJobFunctions>>>) { defines.forEach { job -> kjob.register(job.first, job.second) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessorService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessorService.kt new file mode 100644 index 00000000..07798007 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessorService.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.core.service.job + +import dev.usbharu.hideout.core.external.job.HideoutJob + +interface JobProcessorService> { + suspend fun process(param: T) + suspend fun job(): Class +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobQueueWorkerService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobQueueWorkerService.kt index 982dbeb0..9496470e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobQueueWorkerService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobQueueWorkerService.kt @@ -8,7 +8,7 @@ import kjob.core.dsl.JobRegisterContext as JRC @Service interface JobQueueWorkerService { - fun init( - defines: List>.(HJ) -> KJobFunctions>>> + fun > init( + defines: List>.(R) -> KJobFunctions>>> ) } From 25b689b73ae15c20099a4c57ddd52a5b3b18d4f8 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 26 Nov 2023 12:40:59 +0900 Subject: [PATCH 019/116] =?UTF-8?q?feat:=20=E5=9E=8B=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E3=82=B8=E3=83=A7=E3=83=96=E3=82=AD=E3=83=A5=E3=83=BC=E3=81=AE?= =?UTF-8?q?=E9=AA=A8=E7=B5=84=E3=81=BF=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/service/common/ApJobService.kt | 2 +- .../activitypub/service/common/ApJobServiceImpl.kt | 5 ++++- .../hideout/application/config/JobQueueRunner.kt | 7 +++++-- .../usbharu/hideout/core/external/job/HideoutJob.kt | 7 ++++--- .../kjobexposed/KJobJobQueueWorkerService.kt | 12 +++++++++++- .../kjobmongodb/KJobMongoJobQueueWorkerService.kt | 8 +++----- .../kjobmongodb/KjobMongoJobQueueParentService.kt | 6 ++++++ .../usbharu/hideout/core/service/job/JobProcessor.kt | 8 ++++++++ .../hideout/core/service/job/JobProcessorService.kt | 8 -------- 9 files changed, 42 insertions(+), 21 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessor.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessorService.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobService.kt index fe909b0d..3b50b777 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobService.kt @@ -4,5 +4,5 @@ import dev.usbharu.hideout.core.external.job.HideoutJob import kjob.core.dsl.JobContextWithProps interface ApJobService { - suspend fun processActivity(job: JobContextWithProps, hideoutJob: HideoutJob) + suspend fun > processActivity(job: JobContextWithProps, hideoutJob: HideoutJob) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt index 5da0356c..150e93a3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt @@ -21,7 +21,10 @@ class ApJobServiceImpl( private val inboxJobProcessor: InboxJobProcessor ) : ApJobService { @Suppress("REDUNDANT_ELSE_IN_WHEN") - override suspend fun processActivity(job: JobContextWithProps, hideoutJob: HideoutJob) { + override suspend fun > processActivity( + job: JobContextWithProps, + hideoutJob: HideoutJob + ) { logger.debug("processActivity: ${hideoutJob.name}") @Suppress("ElseCaseInsteadOfExhaustiveWhen") diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/JobQueueRunner.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/JobQueueRunner.kt index 4f7f1aaf..bb22e6d6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/JobQueueRunner.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/JobQueueRunner.kt @@ -11,7 +11,10 @@ import org.springframework.boot.ApplicationRunner import org.springframework.stereotype.Component @Component -class JobQueueRunner(private val jobQueueParentService: JobQueueParentService, private val jobs: List) : +class JobQueueRunner( + private val jobQueueParentService: JobQueueParentService, + private val jobs: List> +) : ApplicationRunner { override fun run(args: ApplicationArguments?) { LOGGER.info("Init job queue. ${jobs.size}") @@ -26,7 +29,7 @@ class JobQueueRunner(private val jobQueueParentService: JobQueueParentService, p @Component class JobQueueWorkerRunner( private val jobQueueWorkerService: JobQueueWorkerService, - private val jobs: List, + private val jobs: List>, private val apJobService: ApJobService ) : ApplicationRunner { override fun run(args: ApplicationArguments?) { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt index 30c02371..72878f6e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt @@ -6,9 +6,10 @@ import kjob.core.dsl.ScheduleContext import kjob.core.job.JobProps import org.springframework.stereotype.Component -abstract class HideoutJob>(name: String = "") : Job(name) { - abstract fun convert(value: T): ScheduleContext.(R) -> Unit - abstract fun convert(props: JobProps): T +abstract class HideoutJob>(name: String = "") : Job(name) { + abstract fun convert(value: @UnsafeVariance T): ScheduleContext<@UnsafeVariance R>.(@UnsafeVariance R) -> Unit + fun convertUnsafe(props: JobProps<*>): T = convert(props as JobProps) + abstract fun convert(props: JobProps<@UnsafeVariance R>): T } data class ReceiveFollowJobParam( diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt index 8404a2db..872a3249 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.core.infrastructure.kjobexposed import dev.usbharu.hideout.core.external.job.HideoutJob +import dev.usbharu.hideout.core.service.job.JobProcessor import dev.usbharu.hideout.core.service.job.JobQueueWorkerService import kjob.core.dsl.JobContextWithProps import kjob.core.dsl.JobRegisterContext @@ -12,7 +13,7 @@ import org.springframework.stereotype.Service @Service @ConditionalOnProperty(name = ["hideout.use-mongodb"], havingValue = "false", matchIfMissing = true) -class KJobJobQueueWorkerService() : JobQueueWorkerService { +class KJobJobQueueWorkerService(private val jobQueueProcessorList: List>) : JobQueueWorkerService { val kjob by lazy { kjob(ExposedKJob) { @@ -27,5 +28,14 @@ class KJobJobQueueWorkerService() : JobQueueWorkerService { defines.forEach { job -> kjob.register(job.first, job.second) } + + for (jobProcessor in jobQueueProcessorList) { + kjob.register(jobProcessor.job()) { + execute { + val param = it.convertUnsafe(props) + jobProcessor.process(param) + } + } + } } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt index 5d4ed22c..25fbdefe 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt @@ -1,15 +1,15 @@ package dev.usbharu.hideout.core.infrastructure.kjobmongodb import com.mongodb.reactivestreams.client.MongoClient +import dev.usbharu.hideout.core.external.job.HideoutJob import dev.usbharu.hideout.core.service.job.JobQueueWorkerService +import kjob.core.dsl.JobContextWithProps import kjob.core.dsl.JobRegisterContext import kjob.core.dsl.KJobFunctions import kjob.core.kjob import kjob.mongo.Mongo import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.stereotype.Service -import dev.usbharu.hideout.core.external.job.HideoutJob as HJ -import kjob.core.dsl.JobContextWithProps as JCWP @Service @ConditionalOnProperty(name = ["hideout.use-mongodb"], havingValue = "true", matchIfMissing = false) @@ -23,9 +23,7 @@ class KJobMongoJobQueueWorkerService(private val mongoClient: MongoClient) : Job }.start() } - override fun init( - defines: List>.(HJ) -> KJobFunctions>>> - ) { + override fun > init(defines: List>.(R) -> KJobFunctions>>>) { defines.forEach { job -> kjob.register(job.first, job.second) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KjobMongoJobQueueParentService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KjobMongoJobQueueParentService.kt index 0875325d..846e8dff 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KjobMongoJobQueueParentService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KjobMongoJobQueueParentService.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.core.infrastructure.kjobmongodb import com.mongodb.reactivestreams.client.MongoClient +import dev.usbharu.hideout.core.external.job.HideoutJob import dev.usbharu.hideout.core.service.job.JobQueueParentService import kjob.core.Job import kjob.core.dsl.ScheduleContext @@ -23,10 +24,15 @@ class KjobMongoJobQueueParentService(private val mongoClient: MongoClient) : Job override fun init(jobDefines: List) = Unit + @Deprecated("use type safe → scheduleTypeSafe") override suspend fun schedule(job: J, block: ScheduleContext.(J) -> Unit) { kjob.schedule(job, block) } + override suspend fun > scheduleTypeSafe(job: J, jobProps: T) { + TODO("Not yet implemented") + } + override fun close() { kjob.shutdown() } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessor.kt new file mode 100644 index 00000000..f6adc74b --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessor.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.core.service.job + +import dev.usbharu.hideout.core.external.job.HideoutJob + +interface JobProcessor> { + suspend fun process(param: @UnsafeVariance T) + fun job(): R +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessorService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessorService.kt deleted file mode 100644 index 07798007..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessorService.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.usbharu.hideout.core.service.job - -import dev.usbharu.hideout.core.external.job.HideoutJob - -interface JobProcessorService> { - suspend fun process(param: T) - suspend fun job(): Class -} From abce56e52df4691344d042ab8098aef61be2f686 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 26 Nov 2023 13:17:49 +0900 Subject: [PATCH 020/116] =?UTF-8?q?feat:=20Inbox=E3=81=AE=E3=82=B8?= =?UTF-8?q?=E3=83=A7=E3=83=96=E3=82=AD=E3=83=A5=E3=83=BC=E3=82=92=E5=9E=8B?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E3=82=B8=E3=83=A7=E3=83=96=E3=82=AD=E3=83=A5?= =?UTF-8?q?=E3=83=BC=E5=AE=9F=E8=A3=85=E3=81=AB=E5=88=87=E3=82=8A=E6=9B=BF?= =?UTF-8?q?=E3=81=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/tmp/InboxJobProcessor.kt | 11 +++++++- .../application/config/JobQueueRunner.kt | 27 ++++++++++--------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt index 99ad3fb9..e33da23b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt @@ -7,7 +7,9 @@ import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.external.job.InboxJob +import dev.usbharu.hideout.core.external.job.InboxJobParam import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.job.JobProcessor import dev.usbharu.hideout.util.RsaUtil import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpRequest @@ -27,7 +29,7 @@ class InboxJobProcessor( private val signatureVerifier: HttpSignatureVerifier, private val userQueryService: UserQueryService, private val apUserService: APUserService -) { +) : JobProcessor { suspend fun process(props: JobProps) { val type = ActivityType.valueOf(props[InboxJob.type]) @@ -90,6 +92,13 @@ class InboxJobProcessor( } } + override suspend fun process(param: InboxJobParam) { + println(param) + System.err.println("aaaaaaaaaaaaaaaaaaaaaaaaaaa") + } + + override fun job(): InboxJob = InboxJob + companion object { private val logger = LoggerFactory.getLogger(InboxJobProcessor::class.java) } diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/JobQueueRunner.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/JobQueueRunner.kt index bb22e6d6..cc1580ea 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/JobQueueRunner.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/JobQueueRunner.kt @@ -34,19 +34,20 @@ class JobQueueWorkerRunner( ) : ApplicationRunner { override fun run(args: ApplicationArguments?) { LOGGER.info("Init job queue worker.") - jobQueueWorkerService.init( - jobs.map { - it to { - execute { - LOGGER.debug("excute job ${it.name}") - apJobService.processActivity( - job = this, - hideoutJob = it - ) - } - } - } - ) +// jobQueueWorkerService.init>( +// jobs.map { +// it to { +// execute { +// LOGGER.debug("excute job ${it.name}") +// apJobService.processActivity( +// job = this, +// hideoutJob = it +// ) +// } +// } +// } +// ) + jobQueueWorkerService.init>(emptyList()) } companion object { From 9171e3a063f802efa4a61f59831acfce85f30e3a Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 26 Nov 2023 15:36:43 +0900 Subject: [PATCH 021/116] =?UTF-8?q?feat:=20Inbox=E3=81=AE=E3=82=B8?= =?UTF-8?q?=E3=83=A7=E3=83=96=E3=82=AD=E3=83=A5=E3=83=BC=E3=82=92=E5=9E=8B?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E3=82=B8=E3=83=A7=E3=83=96=E3=82=AD=E3=83=A5?= =?UTF-8?q?=E3=83=BC=E5=AE=9F=E8=A3=85=E3=81=AB=E5=88=87=E3=82=8A=E6=9B=BF?= =?UTF-8?q?=E3=81=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/tmp/InboxJobProcessor.kt | 32 +++++++++++++++++-- .../hideout/core/external/job/HideoutJob.kt | 7 ++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt index e33da23b..3cc5d0fc 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt @@ -93,8 +93,36 @@ class InboxJobProcessor( } override suspend fun process(param: InboxJobParam) { - println(param) - System.err.println("aaaaaaaaaaaaaaaaaaaaaaaaaaa") + val jsonNode = objectMapper.readTree(param.json) + + logger.info("START Process inbox. type: {}", param.type) + logger.trace("type: {}\njson: \n{}", param.type, jsonNode.toPrettyString()) + + val map = objectMapper.readValue>>(param.headers) + + val httpRequest = objectMapper.readValue(param.httpRequest).copy(headers = HttpHeaders(map)) + + logger.trace("Request: {}\nheaders: {}", httpRequest, map) + + val signature = parseSignatureHeader(httpRequest.headers) + + logger.debug("Has signature? {}", signature != null) + + val verify = signature?.let { verifyHttpSignature(httpRequest, it) } ?: false + + logger.debug("Is verifying success? {}", verify) + + val activityPubProcessor = activityPubProcessorList.firstOrNull { it.isSupported(param.type) } + + if (activityPubProcessor == null) { + logger.warn("ActivityType {} is not support.", param.type) + throw IllegalStateException("ActivityPubProcessor not found.") + } + + val value = objectMapper.treeToValue(jsonNode, activityPubProcessor.type()) + activityPubProcessor.process(ActivityPubProcessContext(value, jsonNode, httpRequest, signature, verify)) + + logger.info("SUCCESS Process inbox. type: {}", param.type) } override fun job(): InboxJob = InboxJob diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt index 72878f6e..7ca2a630 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.core.external.job +import dev.usbharu.hideout.activitypub.service.common.ActivityType import kjob.core.Job import kjob.core.Prop import kjob.core.dsl.ScheduleContext @@ -129,7 +130,7 @@ object DeliverRemoveReactionJob : data class InboxJobParam( val json: String, - val type: String, + val type: ActivityType, val httpRequest: String, val headers: String ) @@ -143,14 +144,14 @@ object InboxJob : HideoutJob("InboxJob") { override fun convert(value: InboxJobParam): ScheduleContext.(InboxJob) -> Unit = { props[json] = value.json - props[type] = value.type + props[type] = value.type.name props[httpRequest] = value.httpRequest props[headers] = value.headers } override fun convert(props: JobProps): InboxJobParam = InboxJobParam( props[json], - props[type], + ActivityType.valueOf(props[type]), props[httpRequest], props[headers] ) From 14998f514d9163485e2f93151a339e018266c79d Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 26 Nov 2023 15:47:40 +0900 Subject: [PATCH 022/116] =?UTF-8?q?refactor:=20tmp=E3=81=8B=E3=82=89?= =?UTF-8?q?=E6=AD=A3=E5=BC=8F=E3=81=AA=E3=83=91=E3=83=83=E3=82=B1=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=81=AB=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../create}/CreateActivityProcessor.kt | 6 +++--- .../{tmp => common}/AbstractActivityPubProcessor.kt | 2 +- .../{tmp => common}/ActivityPubProcessContext.kt | 2 +- .../service/{tmp => common}/ActivityPubProcessor.kt | 3 +-- .../activitypub/service/common/ApJobServiceImpl.kt | 2 +- .../service/{tmp => inbox}/InboxJobProcessor.kt | 10 +++++++--- 6 files changed, 14 insertions(+), 11 deletions(-) rename src/main/kotlin/dev/usbharu/hideout/activitypub/service/{tmp/impl => activity/create}/CreateActivityProcessor.kt (79%) rename src/main/kotlin/dev/usbharu/hideout/activitypub/service/{tmp => common}/AbstractActivityPubProcessor.kt (96%) rename src/main/kotlin/dev/usbharu/hideout/activitypub/service/{tmp => common}/ActivityPubProcessContext.kt (88%) rename src/main/kotlin/dev/usbharu/hideout/activitypub/service/{tmp => common}/ActivityPubProcessor.kt (68%) rename src/main/kotlin/dev/usbharu/hideout/activitypub/service/{tmp => inbox}/InboxJobProcessor.kt (92%) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/impl/CreateActivityProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt similarity index 79% rename from src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/impl/CreateActivityProcessor.kt rename to src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt index 60918d01..5d057b3f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/impl/CreateActivityProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt @@ -1,11 +1,11 @@ -package dev.usbharu.hideout.activitypub.service.tmp.impl +package dev.usbharu.hideout.activitypub.service.activity.create import dev.usbharu.hideout.activitypub.domain.model.Create import dev.usbharu.hideout.activitypub.domain.model.Note +import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor +import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService -import dev.usbharu.hideout.activitypub.service.tmp.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.tmp.ActivityPubProcessContext import dev.usbharu.hideout.application.external.Transaction import org.springframework.stereotype.Service diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt similarity index 96% rename from src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt rename to src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt index c5d927c9..8cddae81 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/AbstractActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt @@ -1,4 +1,4 @@ -package dev.usbharu.hideout.activitypub.service.tmp +package dev.usbharu.hideout.activitypub.service.common import dev.usbharu.hideout.activitypub.domain.exception.ActivityPubProcessException import dev.usbharu.hideout.activitypub.domain.exception.FailedProcessException diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessContext.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessContext.kt similarity index 88% rename from src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessContext.kt rename to src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessContext.kt index 6f45fd20..60a17bb4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessContext.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessContext.kt @@ -1,4 +1,4 @@ -package dev.usbharu.hideout.activitypub.service.tmp +package dev.usbharu.hideout.activitypub.service.common import com.fasterxml.jackson.databind.JsonNode import dev.usbharu.hideout.activitypub.domain.model.objects.Object diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessor.kt similarity index 68% rename from src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessor.kt rename to src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessor.kt index 350f1aea..4bc16f25 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/ActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessor.kt @@ -1,7 +1,6 @@ -package dev.usbharu.hideout.activitypub.service.tmp +package dev.usbharu.hideout.activitypub.service.common import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.service.common.ActivityType interface ActivityPubProcessor { suspend fun process(activity: ActivityPubProcessContext) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt index 150e93a3..64505017 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt @@ -3,8 +3,8 @@ package dev.usbharu.hideout.activitypub.service.common import com.fasterxml.jackson.databind.ObjectMapper import dev.usbharu.hideout.activitypub.service.activity.follow.APReceiveFollowJobService import dev.usbharu.hideout.activitypub.service.activity.like.ApReactionJobService +import dev.usbharu.hideout.activitypub.service.inbox.InboxJobProcessor import dev.usbharu.hideout.activitypub.service.objects.note.ApNoteJobService -import dev.usbharu.hideout.activitypub.service.tmp.InboxJobProcessor import dev.usbharu.hideout.core.external.job.* import kjob.core.dsl.JobContextWithProps import kjob.core.job.JobProps diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt similarity index 92% rename from src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt rename to src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt index 3cc5d0fc..71614aca 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/tmp/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt @@ -1,10 +1,13 @@ -package dev.usbharu.hideout.activitypub.service.tmp +package dev.usbharu.hideout.activitypub.service.inbox import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.readValue import dev.usbharu.hideout.activitypub.domain.model.objects.Object +import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext +import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessor import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.activitypub.service.objects.user.APUserService +import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.external.job.InboxJob import dev.usbharu.hideout.core.external.job.InboxJobParam @@ -28,7 +31,8 @@ class InboxJobProcessor( private val signatureHeaderParser: SignatureHeaderParser, private val signatureVerifier: HttpSignatureVerifier, private val userQueryService: UserQueryService, - private val apUserService: APUserService + private val apUserService: APUserService, + private val transaction: Transaction ) : JobProcessor { suspend fun process(props: JobProps) { @@ -92,7 +96,7 @@ class InboxJobProcessor( } } - override suspend fun process(param: InboxJobParam) { + override suspend fun process(param: InboxJobParam) = transaction.transaction { val jsonNode = objectMapper.readTree(param.json) logger.info("START Process inbox. type: {}", param.type) From 8757d059bed408079250b8d385557351c2cbbdd7 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 Nov 2023 11:57:42 +0900 Subject: [PATCH 023/116] =?UTF-8?q?feat:=20follow=E3=81=AE=E3=82=B8?= =?UTF-8?q?=E3=83=A7=E3=83=96=E3=83=97=E3=83=AD=E3=82=BB=E3=83=83=E3=82=B5?= =?UTF-8?q?=E3=83=BC=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../follow/APReceiveFollowJobProcessor.kt | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt new file mode 100644 index 00000000..91b88b79 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt @@ -0,0 +1,62 @@ +package dev.usbharu.hideout.activitypub.service.activity.follow + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.model.Accept +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.activitypub.service.objects.user.APUserService +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.external.job.ReceiveFollowJob +import dev.usbharu.hideout.core.external.job.ReceiveFollowJobParam +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.job.JobProcessor +import dev.usbharu.hideout.core.service.user.UserService +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class APReceiveFollowJobProcessor( + private val transaction: Transaction, + private val userQueryService: UserQueryService, + private val apUserService: APUserService, + private val objectMapper: ObjectMapper, + private val apRequestService: APRequestService, + private val userService: UserService +) : + JobProcessor { + override suspend fun process(param: ReceiveFollowJobParam) = transaction.transaction { + val person = apUserService.fetchPerson(param.actor, param.targetActor) + val follow = objectMapper.readValue(param.follow) + + + logger.info("START Follow from: {} to {}", param.targetActor, param.actor) + + val signer = userQueryService.findByUrl(param.targetActor) + + val urlString = person.inbox ?: throw IllegalArgumentException("inbox is not found.") + + apRequestService.apPost( + url = urlString, + body = Accept( + name = "Follow", + `object` = follow, + actor = param.targetActor + ), + signer = signer + ) + + val targetEntity = userQueryService.findByUrl(param.targetActor) + val followActorEntity = + userQueryService.findByUrl(follow.actor ?: throw IllegalArgumentException("actor is null")) + + userService.followRequest(targetEntity.id, followActorEntity.id) + logger.info("SUCCESS Follow from: {} to: {}", param.targetActor, param.actor) + } + + override fun job(): ReceiveFollowJob = ReceiveFollowJob + + companion object { + private val logger = LoggerFactory.getLogger(APReceiveFollowJobProcessor::class.java) + } +} From ac4aa8a2316f36dd4a801aece09d9dd2b23b9c44 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:43:37 +0900 Subject: [PATCH 024/116] =?UTF-8?q?feat:=20=E3=81=9D=E3=81=AE=E4=BB=96?= =?UTF-8?q?=E3=81=AEActivityPub=E3=83=97=E3=83=AD=E3=82=BB=E3=83=83?= =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity/accept/APAcceptService.kt | 1 + .../activity/accept/ApAcceptProcessor.kt | 50 +++++++++++++++++ .../activity/delete/APDeleteProcessor.kt | 35 ++++++++++++ .../activity/follow/APFollowProcessor.kt | 35 ++++++++++++ .../follow/APReceiveFollowJobService.kt | 1 + .../follow/APReceiveFollowJobServiceImpl.kt | 1 + .../service/activity/like/APLikeProcessor.kt | 54 +++++++++++++++++++ .../service/activity/undo/APUndoProcessor.kt | 53 ++++++++++++++++++ .../common/AbstractActivityPubProcessor.kt | 2 +- 9 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptService.kt index 2e845715..b702a791 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptService.kt @@ -10,6 +10,7 @@ import dev.usbharu.hideout.core.service.user.UserService import org.slf4j.LoggerFactory import org.springframework.stereotype.Service +@Deprecated("use activitypub processor") interface APAcceptService { suspend fun receiveAccept(accept: Accept) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt new file mode 100644 index 00000000..6060531f --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt @@ -0,0 +1,50 @@ +package dev.usbharu.hideout.activitypub.service.activity.accept + +import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException +import dev.usbharu.hideout.activitypub.domain.model.Accept +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor +import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext +import dev.usbharu.hideout.activitypub.service.common.ActivityType +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.query.FollowerQueryService +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.user.UserService + +class ApAcceptProcessor( + private val transaction: Transaction, + private val userQueryService: UserQueryService, + private val followerQueryService: FollowerQueryService, + private val userService: UserService +) : + AbstractActivityPubProcessor(transaction) { + + override suspend fun internalProcess(activity: ActivityPubProcessContext) { + val value = activity.activity.`object` ?: throw IllegalActivityPubObjectException("object is null") + + if (value.type.contains("Follow").not()) { + logger.warn("FAILED Activity type is not Follow.") + throw IllegalActivityPubObjectException("Invalid type ${value.type}") + } + + val follow = value as Follow + + val userUrl = follow.`object` ?: throw IllegalActivityPubObjectException("object is null") + val followerUrl = follow.actor ?: throw IllegalActivityPubObjectException("actor is null") + + val user = userQueryService.findByUrl(userUrl) + val follower = userQueryService.findByUrl(followerUrl) + + if (followerQueryService.alreadyFollow(user.id, follower.id)) { + logger.debug("END User already follow from ${follower.url} to ${user.url}.") + return + } + + userService.follow(user.id, follower.id) + logger.debug("SUCCESS Follow from ${follower.url} to ${user.url}.") + } + + override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Accept + + override fun type(): Class = Accept::class.java +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt new file mode 100644 index 00000000..fee94bad --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt @@ -0,0 +1,35 @@ +package dev.usbharu.hideout.activitypub.service.activity.delete + +import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException +import dev.usbharu.hideout.activitypub.domain.model.Delete +import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor +import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext +import dev.usbharu.hideout.activitypub.service.common.ActivityType +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.model.post.PostRepository +import dev.usbharu.hideout.core.query.PostQueryService + +class APDeleteProcessor( + transaction: Transaction, + private val postQueryService: PostQueryService, + private val postRepository: PostRepository +) : + AbstractActivityPubProcessor(transaction) { + override suspend fun internalProcess(activity: ActivityPubProcessContext) { + val deleteId = activity.activity.`object`?.id ?: throw IllegalActivityPubObjectException("object.id is null") + + val post = try { + postQueryService.findByApId(deleteId) + } catch (e: FailedToGetResourcesException) { + logger.warn("FAILED delete id: {} is not found.", deleteId) + return + } + + postRepository.delete(post.id) + } + + override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Delete + + override fun type(): Class = Delete::class.java +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt new file mode 100644 index 00000000..92ad3e28 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt @@ -0,0 +1,35 @@ +package dev.usbharu.hideout.activitypub.service.activity.follow + +import com.fasterxml.jackson.databind.ObjectMapper +import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor +import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext +import dev.usbharu.hideout.activitypub.service.common.ActivityType +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.external.job.ReceiveFollowJob +import dev.usbharu.hideout.core.external.job.ReceiveFollowJobParam +import dev.usbharu.hideout.core.service.job.JobQueueParentService + +class APFollowProcessor( + transaction: Transaction, + private val jobQueueParentService: JobQueueParentService, + private val objectMapper: ObjectMapper +) : + AbstractActivityPubProcessor(transaction) { + override suspend fun internalProcess(activity: ActivityPubProcessContext) { + logger.info("FOLLOW from: {} to {}", activity.activity.actor, activity.activity.`object`) + + // inboxをジョブキューに乗せているので既に不要だが、フォロー承認制アカウントを実装する際に必要なので残す + val jobProps = ReceiveFollowJobParam( + activity.activity.actor ?: throw IllegalActivityPubObjectException("actor is null"), + objectMapper.writeValueAsString(activity.activity), + activity.activity.`object` ?: throw IllegalActivityPubObjectException("object is null") + ) + jobQueueParentService.scheduleTypeSafe(ReceiveFollowJob, jobProps) + } + + override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Follow + + override fun type(): Class = Follow::class.java +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobService.kt index 2b7a84d4..d3c106b9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobService.kt @@ -3,6 +3,7 @@ package dev.usbharu.hideout.activitypub.service.activity.follow import dev.usbharu.hideout.core.external.job.ReceiveFollowJob import kjob.core.job.JobProps +@Deprecated("use activitypub processor") interface APReceiveFollowJobService { suspend fun receiveFollowJob(props: JobProps) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobServiceImpl.kt index 02a466ab..86056b69 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobServiceImpl.kt @@ -16,6 +16,7 @@ import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Component @Component +@Deprecated("use activitypub processor") class APReceiveFollowJobServiceImpl( private val apUserService: APUserService, private val userQueryService: UserQueryService, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt new file mode 100644 index 00000000..470eaee7 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt @@ -0,0 +1,54 @@ +package dev.usbharu.hideout.activitypub.service.activity.like + +import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException +import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException +import dev.usbharu.hideout.activitypub.domain.model.Like +import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor +import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext +import dev.usbharu.hideout.activitypub.service.common.ActivityType +import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService +import dev.usbharu.hideout.activitypub.service.objects.user.APUserService +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.query.PostQueryService +import dev.usbharu.hideout.core.service.reaction.ReactionService + +class APLikeProcessor( + transaction: Transaction, + private val apUserService: APUserService, + private val apNoteService: APNoteService, + private val postQueryService: PostQueryService, + private val reactionService: ReactionService +) : + AbstractActivityPubProcessor(transaction) { + override suspend fun internalProcess(activity: ActivityPubProcessContext) { + val actor = activity.activity.actor ?: throw IllegalActivityPubObjectException("actor is null") + val content = activity.activity.content ?: throw IllegalActivityPubObjectException("content is null") + + val target = activity.activity.`object` ?: throw IllegalActivityPubObjectException("object is null") + + val personWithEntity = apUserService.fetchPersonWithEntity(actor) + + try { + apNoteService.fetchNoteAsync(target).await() + } catch (e: FailedToGetActivityPubResourceException) { + logger.debug("FAILED failed to get {}", target) + logger.trace("", e) + return + } + + val post = postQueryService.findByUrl(target) + + reactionService.receiveReaction( + content, + actor.substringAfter("://").substringBefore("/"), + personWithEntity.second.id, + post.id + ) + + logger.debug("SUCCESS Add Like($content) from ${personWithEntity.second.url} to ${post.url}") + } + + override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Like + + override fun type(): Class = Like::class.java +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt new file mode 100644 index 00000000..8c09b54f --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt @@ -0,0 +1,53 @@ +package dev.usbharu.hideout.activitypub.service.activity.undo + +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.domain.model.Undo +import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor +import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext +import dev.usbharu.hideout.activitypub.service.common.ActivityType +import dev.usbharu.hideout.activitypub.service.objects.user.APUserService +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.user.UserService + +class APUndoProcessor( + transaction: Transaction, + private val apUserService: APUserService, + private val userQueryService: UserQueryService, + private val userService: UserService +) : + AbstractActivityPubProcessor(transaction) { + override suspend fun internalProcess(activity: ActivityPubProcessContext) { + val undo = activity.activity + if (undo.actor == null) { + return + } + + val type = + undo.`object`?.type.orEmpty() + .firstOrNull { it == "Block" || it == "Follow" || it == "Like" || it == "Announce" || it == "Accept" } + ?: return + + when (type) { + "Follow" -> { + val follow = undo.`object` as Follow + + if (follow.`object` == null) { + return + } + apUserService.fetchPerson(undo.actor!!, follow.`object`) + val follower = userQueryService.findByUrl(undo.actor!!) + val target = userQueryService.findByUrl(follow.`object`!!) + userService.unfollow(target.id, follower.id) + return + } + + else -> {} + } + TODO() + } + + override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Undo + + override fun type(): Class = Undo::class.java +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt index 8cddae81..bfc7d24e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt @@ -11,7 +11,7 @@ abstract class AbstractActivityPubProcessor( private val transaction: Transaction, private val allowUnauthorized: Boolean = false ) : ActivityPubProcessor { - private val logger = LoggerFactory.getLogger(this::class.java) + protected val logger = LoggerFactory.getLogger(this::class.java) override suspend fun process(activity: ActivityPubProcessContext) { if (activity.isAuthorized.not() && allowUnauthorized.not()) { From 3243a0126ff2a4f8c9a6544d26dceb7d828b712b Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:52:42 +0900 Subject: [PATCH 025/116] =?UTF-8?q?feat:=20=E4=B8=8D=E8=A6=81=E3=81=AB?= =?UTF-8?q?=E3=81=AA=E3=81=A3=E3=81=9FAPService=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity/accept/APAcceptService.kt | 55 ---------------- .../activity/create/APCreateService.kt | 40 ------------ .../activity/delete/APReceiveDeleteService.kt | 7 --- .../delete/APReceiveDeleteServiceImpl.kt | 28 --------- .../service/activity/like/APLikeService.kt | 63 ------------------- .../service/activity/undo/APUndoService.kt | 53 ---------------- 6 files changed, 246 deletions(-) delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptService.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateService.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteService.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteServiceImpl.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeService.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoService.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptService.kt deleted file mode 100644 index b702a791..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptService.kt +++ /dev/null @@ -1,55 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.accept - -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException -import dev.usbharu.hideout.activitypub.domain.model.Accept -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.hideout.core.query.UserQueryService -import dev.usbharu.hideout.core.service.user.UserService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Deprecated("use activitypub processor") -interface APAcceptService { - suspend fun receiveAccept(accept: Accept) -} - -@Service -class APAcceptServiceImpl( - private val userService: UserService, - private val userQueryService: UserQueryService, - private val followerQueryService: FollowerQueryService, - private val transaction: Transaction -) : APAcceptService { - override suspend fun receiveAccept(accept: Accept) { - return transaction.transaction { - LOGGER.debug("START Follow") - LOGGER.trace("{}", accept) - val value = accept.`object` ?: throw IllegalActivityPubObjectException("object is null") - if (value.type.contains("Follow").not()) { - LOGGER.warn("FAILED Activity type is not 'Follow'") - throw IllegalActivityPubObjectException("Invalid type ${value.type}") - } - - val follow = value as Follow - val userUrl = follow.`object` ?: throw IllegalActivityPubObjectException("object is null") - val followerUrl = follow.actor ?: throw IllegalActivityPubObjectException("actor is null") - - val user = userQueryService.findByUrl(userUrl) - val follower = userQueryService.findByUrl(followerUrl) - - if (followerQueryService.alreadyFollow(user.id, follower.id)) { - LOGGER.debug("END User already follow from ${follower.url} to ${user.url}") - return@transaction - } - userService.follow(user.id, follower.id) - LOGGER.debug("SUCCESS Follow from ${follower.url} to ${user.url}.") - - } - } - - companion object { - private val LOGGER = LoggerFactory.getLogger(APAcceptServiceImpl::class.java) - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateService.kt deleted file mode 100644 index afcf4881..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateService.kt +++ /dev/null @@ -1,40 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.create - -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException -import dev.usbharu.hideout.activitypub.domain.model.Create -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService -import dev.usbharu.hideout.application.external.Transaction -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -interface APCreateService { - suspend fun receiveCreate(create: Create) -} - -@Service -class APCreateServiceImpl( - private val apNoteService: APNoteService, - private val transaction: Transaction -) : APCreateService { - override suspend fun receiveCreate(create: Create) { - LOGGER.debug("START Create new remote note.") - LOGGER.trace("{}", create) - - val value = create.`object` ?: throw IllegalActivityPubObjectException("object is null") - if (value.type.contains("Note").not()) { - LOGGER.warn("FAILED Object type is not 'Note'") - throw IllegalActivityPubObjectException("object is not Note") - } - - return transaction.transaction { - val note = value as Note - apNoteService.fetchNote(note) - LOGGER.debug("SUCCESS Create new remote note. ${note.id} by ${note.attributedTo}") - } - } - - companion object { - private val LOGGER = LoggerFactory.getLogger(APCreateServiceImpl::class.java) - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteService.kt deleted file mode 100644 index 68505f22..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteService.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.delete - -import dev.usbharu.hideout.activitypub.domain.model.Delete - -interface APReceiveDeleteService { - suspend fun receiveDelete(delete: Delete) -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteServiceImpl.kt deleted file mode 100644 index e75fa90c..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APReceiveDeleteServiceImpl.kt +++ /dev/null @@ -1,28 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.delete - -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException -import dev.usbharu.hideout.activitypub.domain.model.Delete -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.query.PostQueryService -import org.springframework.stereotype.Service - -@Service -class APReceiveDeleteServiceImpl( - private val postQueryService: PostQueryService, - private val postRepository: PostRepository, - private val transaction: Transaction -) : APReceiveDeleteService { - override suspend fun receiveDelete(delete: Delete) = transaction.transaction { - val deleteId = delete.`object`?.id ?: throw IllegalActivityPubObjectException("object.id is null") - - val post = try { - postQueryService.findByApId(deleteId) - } catch (_: FailedToGetResourcesException) { - return@transaction - } - postRepository.delete(post.id) - return@transaction - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeService.kt deleted file mode 100644 index 5b59550e..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeService.kt +++ /dev/null @@ -1,63 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.like - -import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException -import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.query.PostQueryService -import dev.usbharu.hideout.core.service.reaction.ReactionService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -interface APLikeService { - suspend fun receiveLike(like: Like) -} - -@Service -class APLikeServiceImpl( - private val reactionService: ReactionService, - private val apUserService: APUserService, - private val apNoteService: APNoteService, - private val postQueryService: PostQueryService, - private val transaction: Transaction -) : APLikeService { - override suspend fun receiveLike(like: Like) { - LOGGER.debug("START Add Like") - LOGGER.trace("{}", like) - - val actor = like.actor ?: throw IllegalActivityPubObjectException("actor is null") - val content = like.content ?: throw IllegalActivityPubObjectException("content is null") - like.`object` ?: throw IllegalActivityPubObjectException("object is null") - transaction.transaction { - LOGGER.trace("FETCH Liked Person $actor") - val person = apUserService.fetchPersonWithEntity(actor) - LOGGER.trace("{}", person.second) - - LOGGER.trace("FETCH Liked Note ${like.`object`}") - try { - apNoteService.fetchNoteAsync(like.`object` ?: return@transaction).await() - } catch (e: FailedToGetActivityPubResourceException) { - LOGGER.debug("FAILED Failed to Get ${like.`object`}") - LOGGER.trace("", e) - return@transaction - } - val post = postQueryService.findByUrl(like.`object` ?: return@transaction) - LOGGER.trace("{}", post) - - reactionService.receiveReaction( - content, - actor.substringAfter("://").substringBefore("/"), - person.second.id, - post.id - ) - LOGGER.debug("SUCCESS Add Like($content) from ${person.second.url} to ${post.url}") - } - return - } - - companion object { - private val LOGGER = LoggerFactory.getLogger(APLikeServiceImpl::class.java) - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoService.kt deleted file mode 100644 index e0348b6c..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoService.kt +++ /dev/null @@ -1,53 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.undo - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.Undo -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.query.UserQueryService -import dev.usbharu.hideout.core.service.user.UserService -import org.springframework.stereotype.Service - -interface APUndoService { - suspend fun receiveUndo(undo: Undo) -} - -@Service -@Suppress("UnsafeCallOnNullableType") -class APUndoServiceImpl( - private val userService: UserService, - private val apUserService: APUserService, - private val userQueryService: UserQueryService, - private val transaction: Transaction -) : APUndoService { - override suspend fun receiveUndo(undo: Undo) { - if (undo.actor == null) { - return - } - - val type = - undo.`object`?.type.orEmpty() - .firstOrNull { it == "Block" || it == "Follow" || it == "Like" || it == "Announce" || it == "Accept" } - ?: return - - when (type) { - "Follow" -> { - val follow = undo.`object` as Follow - - if (follow.`object` == null) { - return - } - transaction.transaction { - apUserService.fetchPerson(undo.actor!!, follow.`object`) - val follower = userQueryService.findByUrl(undo.actor!!) - val target = userQueryService.findByUrl(follow.`object`!!) - userService.unfollow(target.id, follower.id) - } - return - } - - else -> {} - } - TODO() - } -} From 1b721c5a0c660227425a7028b0956fcd3d1069a0 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:09:44 +0900 Subject: [PATCH 026/116] =?UTF-8?q?feat:=20=E3=81=9D=E3=81=AE=E4=BB=96?= =?UTF-8?q?=E3=81=AEJobProcessor=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity/like/ApReactionJobProcessor.kt | 35 +++++++++++++++ .../like/ApRemoveReactionJobProcessor.kt | 43 +++++++++++++++++++ .../objects/note/ApNoteJobProcessor.kt | 40 +++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobProcessor.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt new file mode 100644 index 00000000..af3f2f09 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt @@ -0,0 +1,35 @@ +package dev.usbharu.hideout.activitypub.service.activity.like + +import dev.usbharu.hideout.activitypub.domain.model.Like +import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.external.job.DeliverReactionJob +import dev.usbharu.hideout.core.external.job.DeliverReactionJobParam +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.job.JobProcessor + +class ApReactionJobProcessor( + private val userQueryService: UserQueryService, + private val apRequestService: APRequestService, + private val applicationConfig: ApplicationConfig, + private val transaction: Transaction +) : JobProcessor { + override suspend fun process(param: DeliverReactionJobParam): Unit = transaction.transaction { + val signer = userQueryService.findByUrl(param.actor) + + apRequestService.apPost( + param.inbox, + Like( + name = "Like", + actor = param.actor, + `object` = param.postUrl, + id = "${applicationConfig.url}/liek/note/${param.id}", + content = param.reaction + ), + signer + ) + } + + override fun job(): DeliverReactionJob = DeliverReactionJob +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt new file mode 100644 index 00000000..dadbe66e --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt @@ -0,0 +1,43 @@ +package dev.usbharu.hideout.activitypub.service.activity.like + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.model.Like +import dev.usbharu.hideout.activitypub.domain.model.Undo +import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJob +import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJobParam +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.job.JobProcessor +import java.time.Instant + +class ApRemoveReactionJobProcessor( + private val userQueryService: UserQueryService, + private val transaction: Transaction, + private val objectMapper: ObjectMapper, + private val apRequestService: APRequestService, + private val applicationConfig: ApplicationConfig +) : JobProcessor { + override suspend fun process(param: DeliverRemoveReactionJobParam): Unit = transaction.transaction { + + val like = objectMapper.readValue(param.like) + + val signer = userQueryService.findByUrl(param.actor) + + apRequestService.apPost( + param.inbox, + Undo( + name = "Undo Reaction", + actor = param.actor, + `object` = like, + id = "${applicationConfig.url}/undo/like/${param.id}", + published = Instant.now() + ), + signer + ) + } + + override fun job(): DeliverRemoveReactionJob = DeliverRemoveReactionJob +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobProcessor.kt new file mode 100644 index 00000000..df54350d --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobProcessor.kt @@ -0,0 +1,40 @@ +package dev.usbharu.hideout.activitypub.service.objects.note + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.model.Create +import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.external.job.DeliverPostJob +import dev.usbharu.hideout.core.external.job.DeliverPostJobParam +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.job.JobProcessor +import org.slf4j.LoggerFactory + +class ApNoteJobProcessor( + private val transaction: Transaction, + private val objectMapper: ObjectMapper, + private val userQueryService: UserQueryService, + private val apRequestService: APRequestService +) : JobProcessor { + override suspend fun process(param: DeliverPostJobParam) { + val create = objectMapper.readValue(param.create) + transaction.transaction { + val signer = userQueryService.findByUrl(param.actor) + + logger.debug("CreateNoteJob: actor: {} create: {} inbox: {}", param.actor, create, param.inbox) + + apRequestService.apPost( + param.inbox, + create, + signer + ) + } + } + + override fun job(): DeliverPostJob = DeliverPostJob + + companion object { + private val logger = LoggerFactory.getLogger(ApNoteJobProcessor::class.java) + } +} From 640dff53cf6881c1d7fe1617319a0438b23f713c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:13:08 +0900 Subject: [PATCH 027/116] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AB?= =?UTF-8?q?=E3=81=AA=E3=81=A3=E3=81=9FAPService=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../follow/APReceiveFollowJobService.kt | 9 --- .../follow/APReceiveFollowJobServiceImpl.kt | 62 ----------------- .../activity/like/ApReactionJobService.kt | 10 --- .../activity/like/ApReactionJobServiceImpl.kt | 66 ------------------- .../service/common/ApJobServiceImpl.kt | 58 ---------------- .../service/objects/note/ApNoteJobService.kt | 8 --- .../objects/note/ApNoteJobServiceImpl.kt | 41 ------------ 7 files changed, 254 deletions(-) delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobService.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobServiceImpl.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobService.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobServiceImpl.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobService.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobServiceImpl.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobService.kt deleted file mode 100644 index d3c106b9..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobService.kt +++ /dev/null @@ -1,9 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.follow - -import dev.usbharu.hideout.core.external.job.ReceiveFollowJob -import kjob.core.job.JobProps - -@Deprecated("use activitypub processor") -interface APReceiveFollowJobService { - suspend fun receiveFollowJob(props: JobProps) -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobServiceImpl.kt deleted file mode 100644 index 86056b69..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobServiceImpl.kt +++ /dev/null @@ -1,62 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.follow - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.activitypub.domain.model.Accept -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.external.job.ReceiveFollowJob -import dev.usbharu.hideout.core.query.UserQueryService -import dev.usbharu.hideout.core.service.user.UserService -import kjob.core.job.JobProps -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.stereotype.Component - -@Component -@Deprecated("use activitypub processor") -class APReceiveFollowJobServiceImpl( - private val apUserService: APUserService, - private val userQueryService: UserQueryService, - private val apRequestService: APRequestService, - private val userService: UserService, - @Qualifier("activitypub") private val objectMapper: ObjectMapper, - private val transaction: Transaction -) : APReceiveFollowJobService { - override suspend fun receiveFollowJob(props: JobProps) { - transaction.transaction { - val actor = props[ReceiveFollowJob.actor] - val targetActor = props[ReceiveFollowJob.targetActor] - val person = apUserService.fetchPerson(actor, targetActor) - val follow = objectMapper.readValue(props[ReceiveFollowJob.follow]) - logger.info("START Follow from: {} to: {}", targetActor, actor) - - val signer = userQueryService.findByUrl(targetActor) - - val urlString = person.inbox ?: throw IllegalArgumentException("inbox is not found") - - apRequestService.apPost( - url = urlString, - body = Accept( - name = "Follow", - `object` = follow, - actor = targetActor - ), - signer = signer - ) - - val targetEntity = userQueryService.findByUrl(targetActor) - val followActorEntity = - userQueryService.findByUrl(follow.actor ?: throw java.lang.IllegalArgumentException("Actor is null")) - - userService.followRequest(targetEntity.id, followActorEntity.id) - logger.info("SUCCESS Follow from: {} to: {}", targetActor, actor) - } - } - - companion object { - private val logger = LoggerFactory.getLogger(APReceiveFollowJobServiceImpl::class.java) - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobService.kt deleted file mode 100644 index ca43443f..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobService.kt +++ /dev/null @@ -1,10 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.like - -import dev.usbharu.hideout.core.external.job.DeliverReactionJob -import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJob -import kjob.core.job.JobProps - -interface ApReactionJobService { - suspend fun reactionJob(props: JobProps) - suspend fun removeReactionJob(props: JobProps) -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobServiceImpl.kt deleted file mode 100644 index 5a8e088c..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobServiceImpl.kt +++ /dev/null @@ -1,66 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.like - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.activitypub.domain.model.Undo -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.external.job.DeliverReactionJob -import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJob -import dev.usbharu.hideout.core.query.UserQueryService -import kjob.core.job.JobProps -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -class ApReactionJobServiceImpl( - private val userQueryService: UserQueryService, - private val apRequestService: APRequestService, - private val applicationConfig: ApplicationConfig, - @Qualifier("activitypub") private val objectMapper: ObjectMapper -) : ApReactionJobService { - override suspend fun reactionJob(props: JobProps) { - val inbox = props[DeliverReactionJob.inbox] - val actor = props[DeliverReactionJob.actor] - val postUrl = props[DeliverReactionJob.postUrl] - val id = props[DeliverReactionJob.id] - val content = props[DeliverReactionJob.reaction] - - val signer = userQueryService.findByUrl(actor) - - apRequestService.apPost( - inbox, - Like( - name = "Like", - actor = actor, - `object` = postUrl, - id = "${applicationConfig.url}/like/note/$id", - content = content - ), - signer - ) - } - - override suspend fun removeReactionJob(props: JobProps) { - val inbox = props[DeliverRemoveReactionJob.inbox] - val actor = props[DeliverRemoveReactionJob.actor] - val like = objectMapper.readValue(props[DeliverRemoveReactionJob.like]) - val id = props[DeliverRemoveReactionJob.id] - - val signer = userQueryService.findByUrl(actor) - - apRequestService.apPost( - inbox, - Undo( - name = "Undo Reaction", - actor = actor, - `object` = like, - id = "${applicationConfig.url}/undo/note/$id", - published = Instant.now() - ), - signer - ) - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt deleted file mode 100644 index 64505017..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobServiceImpl.kt +++ /dev/null @@ -1,58 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.common - -import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.activitypub.service.activity.follow.APReceiveFollowJobService -import dev.usbharu.hideout.activitypub.service.activity.like.ApReactionJobService -import dev.usbharu.hideout.activitypub.service.inbox.InboxJobProcessor -import dev.usbharu.hideout.activitypub.service.objects.note.ApNoteJobService -import dev.usbharu.hideout.core.external.job.* -import kjob.core.dsl.JobContextWithProps -import kjob.core.job.JobProps -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.stereotype.Service - -@Service -class ApJobServiceImpl( - private val apReceiveFollowJobService: APReceiveFollowJobService, - private val apNoteJobService: ApNoteJobService, - private val apReactionJobService: ApReactionJobService, - @Qualifier("activitypub") private val objectMapper: ObjectMapper, - private val inboxJobProcessor: InboxJobProcessor -) : ApJobService { - @Suppress("REDUNDANT_ELSE_IN_WHEN") - override suspend fun > processActivity( - job: JobContextWithProps, - hideoutJob: HideoutJob - ) { - logger.debug("processActivity: ${hideoutJob.name}") - - @Suppress("ElseCaseInsteadOfExhaustiveWhen") - // Springで作成されるプロキシの都合上パターンマッチングが壊れるので必須 - when (hideoutJob) { - is InboxJob -> { - inboxJobProcessor.process(job.props as JobProps) - } - - is ReceiveFollowJob -> { - apReceiveFollowJobService.receiveFollowJob( - job.props as JobProps - ) - } - - is DeliverPostJob -> apNoteJobService.createNoteJob(job.props as JobProps) - is DeliverReactionJob -> apReactionJobService.reactionJob(job.props as JobProps) - is DeliverRemoveReactionJob -> apReactionJobService.removeReactionJob( - job.props as JobProps - ) - - else -> { - throw IllegalStateException("WTF") - } - } - } - - companion object { - private val logger = LoggerFactory.getLogger(ApJobServiceImpl::class.java) - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobService.kt deleted file mode 100644 index ad7ea01e..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobService.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.objects.note - -import dev.usbharu.hideout.core.external.job.DeliverPostJob -import kjob.core.job.JobProps - -interface ApNoteJobService { - suspend fun createNoteJob(props: JobProps) -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobServiceImpl.kt deleted file mode 100644 index 1e3dc801..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobServiceImpl.kt +++ /dev/null @@ -1,41 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.objects.note - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.activitypub.domain.model.Create -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.external.job.DeliverPostJob -import dev.usbharu.hideout.core.query.UserQueryService -import kjob.core.job.JobProps -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.stereotype.Component - -@Component -class ApNoteJobServiceImpl( - private val userQueryService: UserQueryService, - private val apRequestService: APRequestService, - @Qualifier("activitypub") private val objectMapper: ObjectMapper, - private val transaction: Transaction -) : ApNoteJobService { - override suspend fun createNoteJob(props: JobProps) { - val actor = props[DeliverPostJob.actor] - val create = objectMapper.readValue(props[DeliverPostJob.create]) - transaction.transaction { - val signer = userQueryService.findByUrl(actor) - - val inbox = props[DeliverPostJob.inbox] - logger.debug("createNoteJob: actor={}, create={}, inbox={}", actor, create, inbox) - apRequestService.apPost( - inbox, - create, - signer - ) - } - } - - companion object { - private val logger = LoggerFactory.getLogger(ApNoteJobServiceImpl::class.java) - } -} From a3adba6813ac4cd19d52d1e87e529e73458389e5 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:23:51 +0900 Subject: [PATCH 028/116] =?UTF-8?q?test:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/inbox/InboxControllerImplTest.kt | 32 ++-- .../accept/APAcceptServiceImplTest.kt | 109 ----------- .../create/APCreateServiceImplTest.kt | 63 ------- .../follow/APReceiveFollowServiceImplTest.kt | 176 ------------------ .../activity/like/APLikeServiceImplTest.kt | 111 ----------- .../like/ApReactionJobServiceImplTest.kt | 128 ------------- .../activity/undo/APUndoServiceImplTest.kt | 47 ----- .../service/common/APServiceImplTest.kt | 114 +++--------- 8 files changed, 40 insertions(+), 740 deletions(-) delete mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptServiceImplTest.kt delete mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateServiceImplTest.kt delete mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowServiceImplTest.kt delete mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeServiceImplTest.kt delete mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobServiceImplTest.kt delete mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoServiceImplTest.kt diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt index 1f2ee0a0..4fe4a3b2 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt @@ -4,7 +4,6 @@ import dev.usbharu.hideout.activitypub.domain.exception.JsonParseException import dev.usbharu.hideout.activitypub.service.common.APService import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import io.ktor.http.* import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -12,10 +11,7 @@ import org.junit.jupiter.api.extension.ExtendWith import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.doThrow -import org.mockito.kotlin.eq -import org.mockito.kotlin.whenever +import org.mockito.kotlin.* import org.springframework.http.MediaType import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.get @@ -44,11 +40,15 @@ class InboxControllerImplTest { val json = """{"type":"Follow"}""" whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow) - whenever(apService.processActivity(eq(json), eq(ActivityType.Follow))).doReturn( - ActivityPubStringResponse( - HttpStatusCode.Accepted, "" + whenever( + apService.processActivity( + eq(json), + eq(ActivityType.Follow), + any(), + any() + ) - ) + ).doReturn(Unit) mockMvc .post("/inbox") { @@ -86,7 +86,9 @@ class InboxControllerImplTest { whenever( apService.processActivity( eq(json), - eq(ActivityType.Follow) + eq(ActivityType.Follow), + any(), + any() ) ).doThrow(FailedToGetResourcesException::class) @@ -113,10 +115,8 @@ class InboxControllerImplTest { val json = """{"type":"Follow"}""" whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow) - whenever(apService.processActivity(eq(json), eq(ActivityType.Follow))).doReturn( - ActivityPubStringResponse( - HttpStatusCode.Accepted, "" - ) + whenever(apService.processActivity(eq(json), eq(ActivityType.Follow), any(), any())).doReturn( + Unit ) mockMvc @@ -155,7 +155,9 @@ class InboxControllerImplTest { whenever( apService.processActivity( eq(json), - eq(ActivityType.Follow) + eq(ActivityType.Follow), + any(), + any() ) ).doThrow(FailedToGetResourcesException::class) diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptServiceImplTest.kt deleted file mode 100644 index 8f5ab942..00000000 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APAcceptServiceImplTest.kt +++ /dev/null @@ -1,109 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.accept - -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException -import dev.usbharu.hideout.activitypub.domain.model.Accept -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.hideout.core.query.UserQueryService -import dev.usbharu.hideout.core.service.user.UserService -import io.ktor.http.* -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.mockito.kotlin.* -import utils.TestTransaction -import utils.UserBuilder - -class APAcceptServiceImplTest { - - @Test - fun `receiveAccept 正常なAcceptを処理できる`() = runTest { - val actor = "https://example.com" - val follower = "https://follower.example.com" - val targetUser = UserBuilder.localUserOf() - val followerUser = UserBuilder.localUserOf() - val userQueryService = mock { - onBlocking { findByUrl(eq(actor)) } doReturn targetUser - onBlocking { findByUrl(eq(follower)) } doReturn followerUser - } - val followerQueryService = mock { - onBlocking { alreadyFollow(eq(targetUser.id), eq(followerUser.id)) } doReturn false - } - val userService = mock() - val apAcceptServiceImpl = - APAcceptServiceImpl(userService, userQueryService, followerQueryService, TestTransaction) - - val accept = Accept( - name = "Accept", - `object` = Follow( - name = "", - `object` = actor, - actor = follower - ), - actor = actor - ) - - - val actual = apAcceptServiceImpl.receiveAccept(accept) - assertEquals(ActivityPubStringResponse(HttpStatusCode.OK, "accepted"), actual) - verify(userService, times(1)).follow(eq(targetUser.id), eq(followerUser.id)) - } - - @Test - fun `receiveAccept 既にフォローしている場合は無視する`() = runTest { - - val actor = "https://example.com" - val follower = "https://follower.example.com" - val targetUser = UserBuilder.localUserOf() - val followerUser = UserBuilder.localUserOf() - val userQueryService = mock { - onBlocking { findByUrl(eq(actor)) } doReturn targetUser - onBlocking { findByUrl(eq(follower)) } doReturn followerUser - } - val followerQueryService = mock { - onBlocking { alreadyFollow(eq(targetUser.id), eq(followerUser.id)) } doReturn true - } - val userService = mock() - val apAcceptServiceImpl = - APAcceptServiceImpl(userService, userQueryService, followerQueryService, TestTransaction) - - val accept = Accept( - name = "Accept", - `object` = Follow( - name = "", - `object` = actor, - actor = follower - ), - actor = actor - ) - - - val actual = apAcceptServiceImpl.receiveAccept(accept) - assertEquals(ActivityPubStringResponse(HttpStatusCode.OK, "accepted"), actual) - verify(userService, times(0)).follow(eq(targetUser.id), eq(followerUser.id)) - } - - @Test - fun `revieveAccept AcceptのobjectのtypeがFollow以外の場合IllegalActivityPubObjectExceptionがthrowされる`() = - runTest { - val accept = Accept( - name = "Accept", - `object` = Like( - name = "Like", - actor = "actor", - id = "https://example.com", - `object` = "https://example.com", - content = "aaaa" - ), - actor = "https://example.com" - ) - - val apAcceptServiceImpl = APAcceptServiceImpl(mock(), mock(), mock(), TestTransaction) - - assertThrows { - apAcceptServiceImpl.receiveAccept(accept) - } - } -} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateServiceImplTest.kt deleted file mode 100644 index 5eaff691..00000000 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/APCreateServiceImplTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.create - -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException -import dev.usbharu.hideout.activitypub.domain.model.Create -import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService -import io.ktor.http.* -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.mockito.kotlin.* -import utils.TestTransaction - -class APCreateServiceImplTest { - - @Test - fun `receiveCreate 正常なCreateを処理できる`() = runTest { - val create = Create( - name = "Create", - `object` = Note( - name = "Note", - id = "https://example.com/note", - attributedTo = "https://example.com/actor", - content = "Hello World", - published = "Date: Wed, 21 Oct 2015 07:28:00 GMT" - ), - actor = "https://example.com/actor", - id = "https://example.com/create", - ) - - val apNoteService = mock() - val apCreateServiceImpl = APCreateServiceImpl(apNoteService, TestTransaction) - - val actual = ActivityPubStringResponse(HttpStatusCode.OK, "Created") - - val receiveCreate = apCreateServiceImpl.receiveCreate(create) - verify(apNoteService, times(1)).fetchNote(any(), anyOrNull()) - assertEquals(actual, receiveCreate) - } - - @Test - fun `reveiveCreate CreateのobjectのtypeがNote以外の場合IllegalActivityPubObjectExceptionがthrowされる`() = runTest { - val create = Create( - name = "Create", - `object` = Like( - name = "Like", - id = "https://example.com/note", - actor = "https://example.com/actor", - `object` = "https://example.com/create", - content = "aaa" - ), - actor = "https://example.com/actor", - id = "https://example.com/create", - ) - - val apCreateServiceImpl = APCreateServiceImpl(mock(), TestTransaction) - assertThrows { - apCreateServiceImpl.receiveCreate(create) - } - } -} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowServiceImplTest.kt deleted file mode 100644 index d80183dd..00000000 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowServiceImplTest.kt +++ /dev/null @@ -1,176 +0,0 @@ -@file:OptIn(ExperimentalCoroutinesApi::class) -@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - -package dev.usbharu.hideout.activitypub.service.activity.follow - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.Image -import dev.usbharu.hideout.activitypub.domain.model.Key -import dev.usbharu.hideout.activitypub.domain.model.Person -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.config.CharacterLimit -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.user.User -import dev.usbharu.hideout.core.external.job.ReceiveFollowJob -import dev.usbharu.hideout.core.query.UserQueryService -import dev.usbharu.hideout.core.service.job.JobQueueParentService -import dev.usbharu.hideout.core.service.user.UserService -import kjob.core.dsl.ScheduleContext -import kjob.core.job.JobProps -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runTest -import kotlinx.serialization.json.Json -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.mockito.ArgumentMatchers.anyString -import org.mockito.kotlin.* -import utils.JsonObjectMapper.objectMapper -import utils.TestTransaction -import java.net.URL -import java.time.Instant - -class APReceiveFollowServiceImplTest { - - val userBuilder = User.UserBuilder(CharacterLimit(), ApplicationConfig(URL("https://example.com"))) - val postBuilder = Post.PostBuilder(CharacterLimit()) - - @Test - fun `receiveFollow フォロー受付処理`() = runTest { - val jobQueueParentService = mock { - onBlocking { schedule(eq(ReceiveFollowJob), any()) } doReturn Unit - } - val activityPubFollowService = - APReceiveFollowServiceImpl( - jobQueueParentService, - objectMapper - ) - activityPubFollowService.receiveFollow( - Follow( - emptyList(), - "Follow", - "https://example.com", - "https://follower.example.com" - ) - ) - verify(jobQueueParentService, times(1)).schedule(eq(ReceiveFollowJob), any()) - argumentCaptor.(ReceiveFollowJob) -> Unit> { - verify(jobQueueParentService, times(1)).schedule(eq(ReceiveFollowJob), capture()) - val scheduleContext = ScheduleContext(Json) - firstValue.invoke(scheduleContext, ReceiveFollowJob) - val actor = scheduleContext.props.props[ReceiveFollowJob.actor.name] - val targetActor = scheduleContext.props.props[ReceiveFollowJob.targetActor.name] - val follow = scheduleContext.props.props[ReceiveFollowJob.follow.name] as String - assertEquals("https://follower.example.com", actor) - assertEquals("https://example.com", targetActor) - //language=JSON - assertEquals( - Json.parseToJsonElement( - """{ - "type": "Follow", - "name": "Follow", - "actor": "https://follower.example.com", - "object": "https://example.com" - -}""" - ), - Json.parseToJsonElement(follow) - ) - } - } - - @Test - fun `receiveFollowJob フォロー受付処理のJob`() = runTest { - val person = Person( - type = emptyList(), - name = "follower", - id = "https://follower.example.com", - preferredUsername = "followerUser", - summary = "This user is follower user.", - inbox = "https://follower.example.com/inbox", - outbox = "https://follower.example.com/outbox", - url = "https://follower.example.com", - icon = Image( - type = emptyList(), - name = "https://follower.example.com/image", - mediaType = "image/png", - url = "https://follower.example.com/image" - ), - publicKey = Key( - type = emptyList(), - name = "Public Key", - id = "https://follower.example.com#main-key", - owner = "https://follower.example.com", - publicKeyPem = "BEGIN PUBLIC KEY...END PUBLIC KEY", - ), - followers = "", - following = "" - - ) - val apUserService = mock { - onBlocking { fetchPerson(anyString(), any()) } doReturn person - } - val userQueryService = mock { - onBlocking { findByUrl(eq("https://example.com")) } doReturn - userBuilder.of( - id = 1L, - name = "test", - domain = "example.com", - screenName = "testUser", - description = "This user is test user.", - inbox = "https://example.com/inbox", - outbox = "https://example.com/outbox", - url = "https://example.com", - publicKey = "", - password = "a", - privateKey = "a", - createdAt = Instant.now(), - keyId = "a" - ) - onBlocking { findByUrl(eq("https://follower.example.com")) } doReturn - userBuilder.of( - id = 2L, - name = "follower", - domain = "follower.example.com", - screenName = "followerUser", - description = "This user is test follower user.", - inbox = "https://follower.example.com/inbox", - outbox = "https://follower.example.com/outbox", - url = "https://follower.example.com", - publicKey = "", - createdAt = Instant.now(), - keyId = "a" - ) - } - - val userService = mock { - onBlocking { followRequest(any(), any()) } doReturn false - } - val activityPubFollowService = - APReceiveFollowJobServiceImpl( - apUserService, - userQueryService, - mock(), - userService, - objectMapper, - TestTransaction - ) - activityPubFollowService.receiveFollowJob( - JobProps( - data = mapOf( - ReceiveFollowJob.actor.name to "https://follower.example.com", - ReceiveFollowJob.targetActor.name to "https://example.com", - //language=JSON - ReceiveFollowJob.follow.name to """{ - "type": "Follow", - "name": "Follow", - "object": "https://example.com", - "actor": "https://follower.example.com", - "@context": null -}""" - ), - json = Json - ) - ) - } -} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeServiceImplTest.kt deleted file mode 100644 index 0cb421f5..00000000 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeServiceImplTest.kt +++ /dev/null @@ -1,111 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.like - -import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException -import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.domain.model.Person -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.core.query.PostQueryService -import dev.usbharu.hideout.core.service.reaction.ReactionService -import io.ktor.http.* -import kotlinx.coroutines.async -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.mockito.kotlin.* -import utils.PostBuilder -import utils.TestTransaction -import utils.UserBuilder - - -class APLikeServiceImplTest { - @Test - fun `receiveLike 正常なLikeを処理できる`() = runTest { - val actor = "https://example.com/actor" - val note = "https://example.com/note" - val like = Like( - name = "Like", actor = actor, id = "htps://example.com", `object` = note, content = "aaa" - ) - - val user = UserBuilder.localUserOf() - val apUserService = mock { - onBlocking { fetchPersonWithEntity(eq(actor), anyOrNull()) } doReturn (Person( - name = "TestUser", - id = "https://example.com", - preferredUsername = "Test user", - summary = "test user", - inbox = "https://example.com/inbox", - outbox = "https://example.com/outbox", - url = "https://example.com/", - icon = null, - publicKey = null, - followers = null, - following = null - ) to user) - } - val apNoteService = mock { - on { fetchNoteAsync(eq(note), anyOrNull()) } doReturn async { - Note( - name = "Note", - id = "https://example.com/note", - attributedTo = "https://example.com/actor", - content = "Hello World", - published = "Date: Wed, 21 Oct 2015 07:28:00 GMT", - ) - } - } - val post = PostBuilder.of() - val postQueryService = mock { - onBlocking { findByUrl(eq(note)) } doReturn post - } - val reactionService = mock() - val apLikeServiceImpl = APLikeServiceImpl( - reactionService, apUserService, apNoteService, postQueryService, TestTransaction - ) - - val actual = apLikeServiceImpl.receiveLike(like) - - verify(reactionService, times(1)).receiveReaction(eq("aaa"), eq("example.com"), eq(user.id), eq(post.id)) - assertEquals(ActivityPubStringResponse(HttpStatusCode.OK, ""), actual) - } - - @Test - fun `recieveLike Likeのobjectのurlが取得できないとき何もしない`() = runTest { - val actor = "https://example.com/actor" - val note = "https://example.com/note" - val like = Like( - name = "Like", actor = actor, id = "htps://example.com", `object` = note, content = "aaa" - ) - - val user = UserBuilder.localUserOf() - val apUserService = mock { - onBlocking { fetchPersonWithEntity(eq(actor), anyOrNull()) } doReturn (Person( - name = "TestUser", - id = "https://example.com", - preferredUsername = "Test user", - summary = "test user", - inbox = "https://example.com/inbox", - outbox = "https://example.com/outbox", - url = "https://example.com/", - icon = null, - publicKey = null, - followers = null, - following = null - ) to user) - } - val apNoteService = mock { - on { fetchNoteAsync(eq(note), anyOrNull()) } doThrow FailedToGetActivityPubResourceException() - } - - val reactionService = mock() - val apLikeServiceImpl = APLikeServiceImpl( - reactionService, apUserService, apNoteService, mock(), TestTransaction - ) - - val actual = apLikeServiceImpl.receiveLike(like) - - verify(reactionService, times(0)).receiveReaction(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull()) - assertEquals(ActivityPubStringResponse(HttpStatusCode.OK, ""), actual) - } -} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobServiceImplTest.kt deleted file mode 100644 index 22763ca8..00000000 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobServiceImplTest.kt +++ /dev/null @@ -1,128 +0,0 @@ -@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - -package dev.usbharu.hideout.activitypub.service.activity.like - -import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.activitypub.domain.model.Undo -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.external.job.DeliverReactionJob -import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJob -import dev.usbharu.hideout.core.query.UserQueryService -import kjob.core.job.JobProps -import kotlinx.coroutines.test.runTest -import kotlinx.serialization.json.Json -import org.junit.jupiter.api.Test -import org.mockito.Mockito.mockStatic -import org.mockito.kotlin.* -import utils.JsonObjectMapper.objectMapper -import utils.UserBuilder -import java.net.URL -import java.time.Instant - -class ApReactionJobServiceImplTest { - @Test - fun `reactionJob Likeが配送される`() = runTest { - - val localUser = UserBuilder.localUserOf() - val remoteUser = UserBuilder.remoteUserOf() - - val userQueryService = mock { - onBlocking { findByUrl(localUser.url) } doReturn localUser - } - val apRequestService = mock() - val apReactionJobServiceImpl = ApReactionJobServiceImpl( - userQueryService = userQueryService, - apRequestService = apRequestService, - applicationConfig = ApplicationConfig(URL("https://example.com")), - objectMapper = objectMapper - ) - - - val postUrl = "${remoteUser.url}/posts/1234" - - apReactionJobServiceImpl.reactionJob( - JobProps( - data = mapOf( - DeliverReactionJob.inbox.name to remoteUser.inbox, - DeliverReactionJob.actor.name to localUser.url, - DeliverReactionJob.postUrl.name to postUrl, - DeliverReactionJob.id.name to "1234", - DeliverReactionJob.reaction.name to "❤", - - ), - json = Json - ) - ) - - val body = Like( - name = "Like", - actor = localUser.url, - `object` = postUrl, - id = "https://example.com/like/note/1234", - content = "❤" - ) - - verify(apRequestService, times(1)).apPost(eq(remoteUser.inbox), eq(body), eq(localUser)) - - } - - @Test - fun `removeReactionJob LikeのUndoが配送される`() = runTest { - - val localUser = UserBuilder.localUserOf() - val remoteUser = UserBuilder.remoteUserOf() - - val userQueryService = mock { - onBlocking { findByUrl(localUser.url) } doReturn localUser - } - val apRequestService = mock() - val apReactionJobServiceImpl = ApReactionJobServiceImpl( - userQueryService = userQueryService, - apRequestService = apRequestService, - applicationConfig = ApplicationConfig(URL("https://example.com")), - objectMapper = objectMapper - ) - - - val postUrl = "${remoteUser.url}/posts/1234" - val like = Like( - name = "Like", - actor = remoteUser.url, - `object` = postUrl, - id = "https://example.com/like/note/1234", - content = "❤" - ) - - val now = Instant.now() - - val body = mockStatic(Instant::class.java).use { - - it.`when`(Instant::now).thenReturn(now) - - apReactionJobServiceImpl.removeReactionJob( - JobProps( - data = mapOf( - DeliverRemoveReactionJob.inbox.name to remoteUser.inbox, - DeliverRemoveReactionJob.actor.name to localUser.url, - DeliverRemoveReactionJob.id.name to "1234", - DeliverRemoveReactionJob.like.name to objectMapper.writeValueAsString(like), - - ), - json = Json - ) - ) - Undo( - name = "Undo Reaction", - actor = localUser.url, - `object` = like, - id = "https://example.com/undo/note/1234", - published = now - ) - } - - - - verify(apRequestService, times(1)).apPost(eq(remoteUser.inbox), eq(body), eq(localUser)) - } -} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoServiceImplTest.kt deleted file mode 100644 index e465944e..00000000 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoServiceImplTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.activity.undo - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.Undo -import dev.usbharu.hideout.core.query.UserQueryService -import io.ktor.http.* -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.mock -import utils.TestTransaction -import utils.UserBuilder -import java.time.Instant - -class APUndoServiceImplTest { - @Test - fun `receiveUndo FollowのUndoを処理できる`() = runTest { - - val userQueryService = mock { - onBlocking { findByUrl(eq("https://follower.example.com/actor")) } doReturn UserBuilder.remoteUserOf() - onBlocking { findByUrl(eq("https://example.com/actor")) } doReturn UserBuilder.localUserOf() - } - val apUndoServiceImpl = APUndoServiceImpl( - userService = mock(), - apUserService = mock(), - userQueryService = userQueryService, - transaction = TestTransaction - ) - - val undo = Undo( - name = "Undo", - actor = "https://follower.example.com/actor", - id = "https://follower.example.com/undo/follow", - `object` = Follow( - name = "Follow", - `object` = "https://example.com/actor", - actor = "https://follower.example.com/actor" - ), - published = Instant.now() - ) - val activityPubResponse = apUndoServiceImpl.receiveUndo(undo) - assertEquals(ActivityPubStringResponse(HttpStatusCode.OK, "Accept"), activityPubResponse) - } - -} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APServiceImplTest.kt index 1b6591f8..b4dc586f 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APServiceImplTest.kt @@ -11,13 +11,7 @@ class APServiceImplTest { @Test fun `parseActivity 正常なActivityをパースできる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON @@ -29,13 +23,7 @@ class APServiceImplTest { @Test fun `parseActivity Typeが配列のActivityをパースできる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON @@ -47,13 +35,7 @@ class APServiceImplTest { @Test fun `parseActivity Typeが配列で関係ない物が入っていてもパースできる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON @@ -65,13 +47,8 @@ class APServiceImplTest { @Test fun `parseActivity jsonとして解釈できない場合JsonParseExceptionがthrowされる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON @@ -83,13 +60,8 @@ class APServiceImplTest { @Test fun `parseActivity 空の場合JsonParseExceptionがthrowされる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON @@ -101,13 +73,8 @@ class APServiceImplTest { @Test fun `parseActivity jsonにtypeプロパティがない場合JsonParseExceptionがthrowされる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON @@ -119,13 +86,8 @@ class APServiceImplTest { @Test fun `parseActivity typeが配列でないときtypeが未定義の場合IllegalArgumentExceptionがthrowされる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON @@ -137,13 +99,8 @@ class APServiceImplTest { @Test fun `parseActivity typeが配列のとき定義済みのtypeを見つけられなかった場合IllegalArgumentExceptionがthrowされる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON @@ -155,13 +112,8 @@ class APServiceImplTest { @Test fun `parseActivity typeが空の場合IllegalArgumentExceptionがthrowされる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON @@ -173,13 +125,8 @@ class APServiceImplTest { @Test fun `parseActivity typeに指定されている文字の判定がcase-insensitiveで行われる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON @@ -191,13 +138,8 @@ class APServiceImplTest { @Test fun `parseActivity typeが配列のとき指定されている文字の判定がcase-insensitiveで行われる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON @@ -209,13 +151,8 @@ class APServiceImplTest { @Test fun `parseActivity activityがarrayのときJsonParseExceptionがthrowされる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON @@ -227,13 +164,8 @@ class APServiceImplTest { @Test fun `parseActivity activityがvalueのときJsonParseExceptionがthrowされる`() { val apServiceImpl = APServiceImpl( - apReceiveFollowService = mock(), - apUndoService = mock(), - apAcceptService = mock(), - apCreateService = mock(), - apLikeService = mock(), - apReceiveDeleteService = mock(), - objectMapper = objectMapper + + objectMapper = objectMapper, jobQueueParentService = mock() ) //language=JSON From e40e2c0d969e87a693bfd62da2a4f22778b34514 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:03:26 +0900 Subject: [PATCH 029/116] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E4=BE=9D=E5=AD=98=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/service/common/ApJobService.kt | 8 -------- .../hideout/application/config/JobQueueRunner.kt | 16 ---------------- 2 files changed, 24 deletions(-) delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobService.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobService.kt deleted file mode 100644 index 3b50b777..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ApJobService.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.core.external.job.HideoutJob -import kjob.core.dsl.JobContextWithProps - -interface ApJobService { - suspend fun > processActivity(job: JobContextWithProps, hideoutJob: HideoutJob) -} diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/JobQueueRunner.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/JobQueueRunner.kt index cc1580ea..1667b7ec 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/JobQueueRunner.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/JobQueueRunner.kt @@ -1,6 +1,5 @@ package dev.usbharu.hideout.application.config -import dev.usbharu.hideout.activitypub.service.common.ApJobService import dev.usbharu.hideout.core.external.job.HideoutJob import dev.usbharu.hideout.core.service.job.JobQueueParentService import dev.usbharu.hideout.core.service.job.JobQueueWorkerService @@ -29,24 +28,9 @@ class JobQueueRunner( @Component class JobQueueWorkerRunner( private val jobQueueWorkerService: JobQueueWorkerService, - private val jobs: List>, - private val apJobService: ApJobService ) : ApplicationRunner { override fun run(args: ApplicationArguments?) { LOGGER.info("Init job queue worker.") -// jobQueueWorkerService.init>( -// jobs.map { -// it to { -// execute { -// LOGGER.debug("excute job ${it.name}") -// apJobService.processActivity( -// job = this, -// hideoutJob = it -// ) -// } -// } -// } -// ) jobQueueWorkerService.init>(emptyList()) } From c0dffe7c9842c26cfd38773f24adef5ef7d9a1e4 Mon Sep 17 00:00:00 2001 From: usbharu Date: Mon, 27 Nov 2023 15:10:22 +0900 Subject: [PATCH 030/116] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../service/activity/follow/APReceiveFollowJobProcessor.kt | 1 - .../service/activity/like/ApRemoveReactionJobProcessor.kt | 1 - .../service/common/AbstractActivityPubProcessor.kt | 1 - .../hideout/activitypub/service/inbox/InboxJobProcessor.kt | 1 - .../dev/usbharu/hideout/core/external/job/HideoutJob.kt | 5 +++-- .../infrastructure/kjobexposed/KJobJobQueueWorkerService.kt | 5 ++++- .../kjobmongodb/KJobMongoJobQueueWorkerService.kt | 3 ++- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt index 91b88b79..a6aae23b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt @@ -29,7 +29,6 @@ class APReceiveFollowJobProcessor( val person = apUserService.fetchPerson(param.actor, param.targetActor) val follow = objectMapper.readValue(param.follow) - logger.info("START Follow from: {} to {}", param.targetActor, param.actor) val signer = userQueryService.findByUrl(param.targetActor) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt index dadbe66e..307f0c16 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt @@ -21,7 +21,6 @@ class ApRemoveReactionJobProcessor( private val applicationConfig: ApplicationConfig ) : JobProcessor { override suspend fun process(param: DeliverRemoveReactionJobParam): Unit = transaction.transaction { - val like = objectMapper.readValue(param.like) val signer = userQueryService.findByUrl(param.actor) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt index bfc7d24e..329941b2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt @@ -30,5 +30,4 @@ abstract class AbstractActivityPubProcessor( } abstract suspend fun internalProcess(activity: ActivityPubProcessContext) - } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt index 71614aca..51a49af9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt @@ -35,7 +35,6 @@ class InboxJobProcessor( private val transaction: Transaction ) : JobProcessor { suspend fun process(props: JobProps) { - val type = ActivityType.valueOf(props[InboxJob.type]) val jsonString = objectMapper.readTree(props[InboxJob.json]) val httpRequestString = props[InboxJob.httpRequest] diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt index 7ca2a630..8cd3647f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt @@ -29,7 +29,6 @@ object ReceiveFollowJob : HideoutJob("R props[follow] = value.follow props[actor] = value.actor props[targetActor] = value.targetActor - } override fun convert(props: JobProps): ReceiveFollowJobParam = ReceiveFollowJobParam( @@ -78,7 +77,9 @@ object DeliverReactionJob : HideoutJob = string("actor") val inbox: Prop = string("inbox") val id: Prop = string("id") - override fun convert(value: DeliverReactionJobParam): ScheduleContext.(DeliverReactionJob) -> Unit = + override fun convert( + value: DeliverReactionJobParam + ): ScheduleContext.(DeliverReactionJob) -> Unit = { props[reaction] = value.reaction props[postUrl] = value.postUrl diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt index 872a3249..a03272c4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt @@ -24,7 +24,10 @@ class KJobJobQueueWorkerService(private val jobQueueProcessorList: List> init(defines: List>.(R) -> KJobFunctions>>>) { + override fun > init( + defines: + List>.(R) -> KJobFunctions>>> + ) { defines.forEach { job -> kjob.register(job.first, job.second) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt index 25fbdefe..5017fd0d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt @@ -23,7 +23,8 @@ class KJobMongoJobQueueWorkerService(private val mongoClient: MongoClient) : Job }.start() } - override fun > init(defines: List>.(R) -> KJobFunctions>>>) { + override fun > init(defines: + List>.(R) -> KJobFunctions>>>) { defines.forEach { job -> kjob.register(job.first, job.second) } From e5d1a8d4a66cb744e1d809792ee5e8779cf60735 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:52:49 +0900 Subject: [PATCH 031/116] style: fix lint --- .../interfaces/api/inbox/InboxControllerImpl.kt | 9 +++++---- .../hideout/core/domain/model/instance/Nodeinfo2_0.kt | 2 ++ .../kjobmongodb/KJobMongoJobQueueWorkerService.kt | 6 ++++-- .../dev/usbharu/hideout/core/service/job/JobProcessor.kt | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt index fc8acf7f..1ad9062c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt @@ -13,14 +13,12 @@ import org.springframework.web.context.request.RequestContextHolder import org.springframework.web.context.request.ServletRequestAttributes import java.net.URL - @RestController class InboxControllerImpl(private val apService: APService) : InboxController { @Suppress("TooGenericExceptionCaught") override suspend fun inbox( @RequestBody string: String ): ResponseEntity { - val request = (requireNotNull(RequestContextHolder.getRequestAttributes()) as ServletRequestAttributes).request val parseActivity = try { @@ -48,11 +46,14 @@ class InboxControllerImpl(private val apService: APService) : InboxController { println(headers) apService.processActivity( - string, parseActivity, HttpRequest( + string, + parseActivity, + HttpRequest( URL(url + request.queryString.orEmpty()), HttpHeaders(headers), method - ), headers + ), + headers ) } catch (e: Exception) { LOGGER.warn("FAILED Process Activity $parseActivity", e) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt index fcd99c73..53479eee 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt @@ -1,3 +1,5 @@ +@file:Suppress("Filename") + package dev.usbharu.hideout.core.domain.model.instance @Suppress("ClassNaming") diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt index 5017fd0d..bb48b08b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt @@ -23,8 +23,10 @@ class KJobMongoJobQueueWorkerService(private val mongoClient: MongoClient) : Job }.start() } - override fun > init(defines: - List>.(R) -> KJobFunctions>>>) { + override fun > init( + defines: + List>.(R) -> KJobFunctions>>> + ) { defines.forEach { job -> kjob.register(job.first, job.second) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessor.kt index f6adc74b..7d38449f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/job/JobProcessor.kt @@ -2,7 +2,7 @@ package dev.usbharu.hideout.core.service.job import dev.usbharu.hideout.core.external.job.HideoutJob -interface JobProcessor> { +interface JobProcessor> { suspend fun process(param: @UnsafeVariance T) fun job(): R } From db2046b6e1d2211d71515beedf0bc4bd8ff85bfd Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:04:06 +0900 Subject: [PATCH 032/116] style: fix lint --- .../hideout/activitypub/service/objects/user/APUserService.kt | 1 + .../springframework/httpsignature/HttpSignatureFilter.kt | 2 +- .../oauth2/ExposedOAuth2AuthorizationService.kt | 4 +++- .../service/media/converter/movie/MovieMediaProcessService.kt | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt index 9e8cb4b8..422b4874 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt @@ -77,6 +77,7 @@ class APUserServiceImpl( override suspend fun fetchPerson(url: String, targetActor: String?): Person = fetchPersonWithEntity(url, targetActor).first + @Suppress("LongMethod") override suspend fun fetchPersonWithEntity(url: String, targetActor: String?): Pair { return try { val userEntity = userQueryService.findByUrl(url) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt index 6a68e267..e814e568 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt @@ -37,7 +37,7 @@ class HttpSignatureFilter( transaction.transaction { try { userQueryService.findByKeyId(signature.keyId) - } catch (e: FailedToGetResourcesException) { + } catch (_: FailedToGetResourcesException) { apUserService.fetchPerson(signature.keyId) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt index 458f805c..b18d5ca0 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt @@ -272,7 +272,9 @@ class ExposedOAuth2AuthorizationService( oidcIdTokenValue, oidcTokenIssuedAt, oidcTokenExpiresAt, - oidcTokenMetadata.getValue(OAuth2Authorization.Token.CLAIMS_METADATA_NAME) as MutableMap? + @Suppress("CastToNullableType") + oidcTokenMetadata.getValue(OAuth2Authorization.Token.CLAIMS_METADATA_NAME) + as MutableMap? ) builder.token(oidcIdToken) { it.putAll(oidcTokenMetadata) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/movie/MovieMediaProcessService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/movie/MovieMediaProcessService.kt index 9b39e854..711cf29b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/movie/MovieMediaProcessService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/movie/MovieMediaProcessService.kt @@ -34,6 +34,7 @@ class MovieMediaProcessService : MediaProcessService { TODO("Not yet implemented") } + @Suppress("LongMethod", "NestedBlockDepth", "CognitiveComplexMethod") override suspend fun process( mimeType: MimeType, fileName: String, From 8925c321bd5ae95f32f6b065ad44caf8bab21758 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:29:11 +0900 Subject: [PATCH 033/116] =?UTF-8?q?refactor:=20=E9=95=B7=E3=81=99=E3=81=8E?= =?UTF-8?q?=E3=82=8B=E3=83=A1=E3=82=BD=E3=83=83=E3=83=89=E3=81=AA=E3=81=A9?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/objects/ObjectDeserializer.kt | 11 +- .../service/common/APRequestServiceImpl.kt | 135 +++++++++++------- .../activitypub/service/common/APService.kt | 2 +- .../KjobMongoJobQueueParentService.kt | 3 +- .../hideout/core/service/user/UserService.kt | 1 - 5 files changed, 87 insertions(+), 65 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt index 377dfcff..acbf32e5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt @@ -33,15 +33,8 @@ class ObjectDeserializer : JsonDeserializer() { } return when (activityType) { - ExtendedActivityVocabulary.Follow -> { - val readValue = p.codec.treeToValue(treeNode, Follow::class.java) - readValue - } - - ExtendedActivityVocabulary.Note -> { - p.codec.treeToValue(treeNode, Note::class.java) - } - + ExtendedActivityVocabulary.Follow -> p.codec.treeToValue(treeNode, Follow::class.java) + ExtendedActivityVocabulary.Note -> p.codec.treeToValue(treeNode, Note::class.java) ExtendedActivityVocabulary.Object -> p.codec.treeToValue(treeNode, Object::class.java) ExtendedActivityVocabulary.Link -> TODO() ExtendedActivityVocabulary.Activity -> TODO() diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt index 6e87d402..b2474af1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt @@ -37,15 +37,29 @@ class APRequestServiceImpl( logger.debug("START ActivityPub Request GET url: {}, signer: {}", url, signer?.url) val date = dateTimeFormatter.format(ZonedDateTime.now(ZoneId.of("GMT"))) val u = URL(url) - if (signer?.privateKey == null) { - val bodyAsText = httpClient.get(url) { - header("Accept", ContentType.Application.Activity) - header("Date", date) - }.bodyAsText() - logBody(bodyAsText, url) - return objectMapper.readValue(bodyAsText, responseClass) + val httpResponse = if (signer?.privateKey == null) { + apGetNotSign(url, date) + } else { + apGetSign(date, u, signer, url) } + val bodyAsText = httpResponse.bodyAsText() + val readValue = objectMapper.readValue(bodyAsText, responseClass) + logger.debug( + "SUCCESS ActivityPub Request GET status: {} url: {}", + httpResponse.status, + httpResponse.request.url + ) + logBody(bodyAsText, url) + return readValue + } + + private suspend fun apGetSign( + date: String, + u: URL, + signer: User, + url: String + ): HttpResponse { val headers = headers { append("Accept", ContentType.Application.Activity) append("Date", date) @@ -60,7 +74,7 @@ class APRequestServiceImpl( ), privateKey = PrivateKey( keyId = "${signer.url}#pubkey", - privateKey = RsaUtil.decodeRsaPrivateKeyPem(signer.privateKey), + privateKey = RsaUtil.decodeRsaPrivateKeyPem(signer.privateKey!!), ), signHeaders = listOf("(request-target)", "date", "host", "accept") ) @@ -75,15 +89,12 @@ class APRequestServiceImpl( } contentType(ContentType.Application.Activity) } - val bodyAsText = httpResponse.bodyAsText() - val readValue = objectMapper.readValue(bodyAsText, responseClass) - logger.debug( - "SUCCESS ActivityPub Request GET status: {} url: {}", - httpResponse.status, - httpResponse.request.url - ) - logBody(bodyAsText, url) - return readValue + return httpResponse + } + + private suspend fun apGetNotSign(url: String, date: String?) = httpClient.get(url) { + header("Accept", ContentType.Application.Activity) + header("Date", date) } override suspend fun apPost( @@ -96,18 +107,9 @@ class APRequestServiceImpl( return objectMapper.readValue(bodyAsText, responseClass) } - @Suppress("LongMethod") override suspend fun apPost(url: String, body: T?, signer: User?): String { logger.debug("START ActivityPub Request POST url: {}, signer: {}", url, signer?.url) - val requestBody = if (body != null) { - val mutableListOf = mutableListOf() - mutableListOf.add("https://www.w3.org/ns/activitystreams") - mutableListOf.addAll(body.context) - body.context = mutableListOf - objectMapper.writeValueAsString(body) - } else { - null - } + val requestBody = addContextIfNotNull(body) logger.trace( """ @@ -129,20 +131,45 @@ class APRequestServiceImpl( val date = dateTimeFormatter.format(ZonedDateTime.now(ZoneId.of("GMT"))) val u = URL(url) - if (signer?.privateKey == null) { - val bodyAsText = httpClient.post(url) { - accept(ContentType.Application.Activity) - header("Date", date) - header("Digest", "sha-256=$digest") - if (requestBody != null) { - setBody(requestBody) - contentType(ContentType.Application.Activity) - } - }.bodyAsText() - logBody(bodyAsText, url) - return bodyAsText + val httpResponse = if (signer?.privateKey == null) { + apPostNotSign(url, date, digest, requestBody) + } else { + apPostSign(date, u, digest, signer, url, requestBody) } + val bodyAsText = httpResponse.bodyAsText() + logger.debug( + "SUCCESS ActivityPub Request POST status: {} url: {}", + httpResponse.status, + httpResponse.request.url + ) + logBody(bodyAsText, url) + return bodyAsText + } + + private suspend fun apPostNotSign( + url: String, + date: String?, + digest: String, + requestBody: String? + ) = httpClient.post(url) { + accept(ContentType.Application.Activity) + header("Date", date) + header("Digest", "sha-256=$digest") + if (requestBody != null) { + setBody(requestBody) + contentType(ContentType.Application.Activity) + } + } + + private suspend fun apPostSign( + date: String, + u: URL, + digest: String, + signer: User, + url: String, + requestBody: String? + ): HttpResponse { val headers = headers { append("Accept", ContentType.Application.Activity) append("Date", date) @@ -158,30 +185,32 @@ class APRequestServiceImpl( ), privateKey = PrivateKey( keyId = signer.keyId, - privateKey = RsaUtil.decodeRsaPrivateKeyPem(signer.privateKey) + privateKey = RsaUtil.decodeRsaPrivateKeyPem(signer.privateKey!!) ), signHeaders = listOf("(request-target)", "date", "host", "digest") ) val httpResponse = httpClient.post(url) { headers { - headers { - appendAll(headers) - append("Signature", sign.signatureHeader) - remove("Host") - } + appendAll(headers) + append("Signature", sign.signatureHeader) + remove("Host") + } setBody(requestBody) contentType(ContentType.Application.Activity) } - val bodyAsText = httpResponse.bodyAsText() - logger.debug( - "SUCCESS ActivityPub Request POST status: {} url: {}", - httpResponse.status, - httpResponse.request.url - ) - logBody(bodyAsText, url) - return bodyAsText + return httpResponse + } + + private fun addContextIfNotNull(body: T?) = if (body != null) { + val mutableListOf = mutableListOf() + mutableListOf.add("https://www.w3.org/ns/activitystreams") + mutableListOf.addAll(body.context) + body.context = mutableListOf + objectMapper.writeValueAsString(body) + } else { + null } private fun logBody(bodyAsText: String, url: String) { diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt index e23ed018..0a4745e6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt @@ -218,7 +218,7 @@ class APServiceImpl( } } - @Suppress("CyclomaticComplexMethod", "NotImplementedDeclaration") + @Suppress("CyclomaticComplexMethod") override suspend fun processActivity( json: String, type: ActivityType, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KjobMongoJobQueueParentService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KjobMongoJobQueueParentService.kt index 846e8dff..f66f58ef 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KjobMongoJobQueueParentService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KjobMongoJobQueueParentService.kt @@ -30,7 +30,8 @@ class KjobMongoJobQueueParentService(private val mongoClient: MongoClient) : Job } override suspend fun > scheduleTypeSafe(job: J, jobProps: T) { - TODO("Not yet implemented") + val convert = job.convert(jobProps) + kjob.schedule(job, convert) } override fun close() { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt index fdfb5bcf..fc34c36c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt @@ -3,7 +3,6 @@ package dev.usbharu.hideout.core.service.user import dev.usbharu.hideout.core.domain.model.user.User import org.springframework.stereotype.Service -@Suppress("TooManyFunctions") @Service interface UserService { From 6cf93f0624ae3907918deeb7f80f133e8a36babb Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 Nov 2023 18:28:41 +0900 Subject: [PATCH 034/116] =?UTF-8?q?refactor:=20=E9=95=B7=E3=81=99=E3=81=8E?= =?UTF-8?q?=E3=82=8B=E3=83=A1=E3=82=BD=E3=83=83=E3=83=89=E3=81=AA=E3=81=A9?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- detekt.yml | 6 +- .../activitypub/domain/model/Accept.kt | 1 + .../activitypub/domain/model/Create.kt | 1 + .../activitypub/domain/model/Delete.kt | 1 + .../activitypub/domain/model/Follow.kt | 1 + .../hideout/activitypub/domain/model/Like.kt | 1 + .../hideout/activitypub/domain/model/Undo.kt | 1 + .../exposedquery/NoteQueryServiceImpl.kt | 2 +- .../activity/delete/APDeleteProcessor.kt | 2 +- .../service/common/APRequestServiceImpl.kt | 6 +- .../activitypub/service/common/APService.kt | 1 - .../service/inbox/InboxJobProcessor.kt | 1 + .../objects/note/NoteApApiServiceImpl.kt | 24 ++++-- .../service/objects/user/APUserService.kt | 85 +++++++------------ .../application/config/SecurityConfig.kt | 4 +- .../hideout/core/domain/model/media/Media.kt | 17 ++++ .../core/domain/model/user/UserRepository.kt | 1 - .../hideout/core/external/job/HideoutJob.kt | 4 +- .../infrastructure/exposed/PostQueryMapper.kt | 2 +- .../kjobexposed/KJobJobQueueParentService.kt | 2 +- .../MongoTimelineRepositoryWrapper.kt | 1 - .../ExposedOAuth2AuthorizationService.kt | 3 +- .../image/ImageMediaProcessService.kt | 6 +- .../ExposedGenerateTimelineService.kt | 2 +- .../core/service/user/UserServiceImpl.kt | 1 + .../exposedquery/StatusQueryServiceImpl.kt | 60 ++----------- .../interfaces/api/status/StatusesRequest.kt | 25 +++++- .../service/status/StatusesApiService.kt | 43 ++-------- 28 files changed, 126 insertions(+), 178 deletions(-) diff --git a/detekt.yml b/detekt.yml index c4fee204..a74f1251 100644 --- a/detekt.yml +++ b/detekt.yml @@ -3,9 +3,7 @@ build: weights: Indentation: 0 MagicNumber: 0 - InjectDispatcher: 0 EnumEntryNameCase: 0 - ReplaceSafeCallChainWithRun: 0 VariableNaming: 0 NoNameShadowing: 0 @@ -78,7 +76,7 @@ complexity: active: true ReplaceSafeCallChainWithRun: - active: true + active: false StringLiteralDuplication: active: false @@ -172,3 +170,5 @@ potential-bugs: coroutines: RedundantSuspendModifier: active: false + InjectDispatcher: + active: false diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt index 5f8af943..c249afb4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt @@ -6,6 +6,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer open class Accept : Object { @JsonDeserialize(using = ObjectDeserializer::class) + @Suppress("VariableNaming") var `object`: Object? = null protected constructor() diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt index f81439c6..6b19ab5b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt @@ -6,6 +6,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer open class Create : Object { @JsonDeserialize(using = ObjectDeserializer::class) + @Suppress("VariableNaming") var `object`: Object? = null var to: List = emptyList() var cc: List = emptyList() diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt index 0305fff2..e29e9a93 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt @@ -6,6 +6,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer open class Delete : Object { @JsonDeserialize(using = ObjectDeserializer::class) + @Suppress("VariableNaming") var `object`: Object? = null var published: String? = null diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt index 23648564..00e3eec0 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt @@ -3,6 +3,7 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object open class Follow : Object { + @Suppress("VariableNaming") var `object`: String? = null protected constructor() : super() diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt index 17e1037d..c916f566 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt @@ -5,6 +5,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer open class Like : Object { + @Suppress("VariableNaming") var `object`: String? = null var content: String? = null diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt index 41cc0aa8..a8fbb65a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt @@ -8,6 +8,7 @@ import java.time.Instant open class Undo : Object { @JsonDeserialize(using = ObjectDeserializer::class) + @Suppress("VariableNaming") var `object`: Object? = null var published: String? = null diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt index fd516349..3348f832 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt @@ -80,7 +80,7 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v private suspend fun Query.toNote(): Note { return this.groupBy { it[Posts.id] } .map { it.value } - .map { it.first().toNote(it.mapNotNull { it.toMediaOrNull() }) } + .map { it.first().toNote(it.mapNotNull { resultRow -> resultRow.toMediaOrNull() }) } .singleOr { FailedToGetResourcesException("resource does not exist.") } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt index fee94bad..060dc713 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt @@ -22,7 +22,7 @@ class APDeleteProcessor( val post = try { postQueryService.findByApId(deleteId) } catch (e: FailedToGetResourcesException) { - logger.warn("FAILED delete id: {} is not found.", deleteId) + logger.warn("FAILED delete id: {} is not found.", deleteId, e) return } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt index b2474af1..bf13b1ca 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt @@ -134,7 +134,7 @@ class APRequestServiceImpl( val httpResponse = if (signer?.privateKey == null) { apPostNotSign(url, date, digest, requestBody) } else { - apPostSign(date, u, digest, signer, url, requestBody) + apPostSign(date, u, digest, signer, requestBody) } val bodyAsText = httpResponse.bodyAsText() @@ -167,7 +167,6 @@ class APRequestServiceImpl( u: URL, digest: String, signer: User, - url: String, requestBody: String? ): HttpResponse { val headers = headers { @@ -190,12 +189,11 @@ class APRequestServiceImpl( signHeaders = listOf("(request-target)", "date", "host", "digest") ) - val httpResponse = httpClient.post(url) { + val httpResponse = httpClient.post(u) { headers { appendAll(headers) append("Signature", sign.signatureHeader) remove("Host") - } setBody(requestBody) contentType(ContentType.Application.Activity) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt index 0a4745e6..4f5c2902 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt @@ -218,7 +218,6 @@ class APServiceImpl( } } - @Suppress("CyclomaticComplexMethod") override suspend fun processActivity( json: String, type: ActivityType, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt index 51a49af9..fc68f7fa 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt @@ -86,6 +86,7 @@ class InboxJobProcessor( return verify.success } + @Suppress("TooGenericExceptionCaught") private fun parseSignatureHeader(httpHeaders: HttpHeaders): Signature? { return try { signatureHeaderParser.parse(httpHeaders) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt index 547befbf..079cc073 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt @@ -4,6 +4,7 @@ import dev.usbharu.hideout.activitypub.domain.model.Note import dev.usbharu.hideout.activitypub.query.NoteQueryService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.query.FollowerQueryService import org.slf4j.LoggerFactory @@ -28,20 +29,27 @@ class NoteApApiServiceImpl( } Visibility.FOLLOWERS -> { - if (userId == null) { - return@transaction null - } - - if (followerQueryService.alreadyFollow(findById.second.userId, userId).not()) { - return@transaction null - } - return@transaction findById.first + return@transaction getFollowersNote(userId, findById) } Visibility.DIRECT -> return@transaction null } } + private suspend fun getFollowersNote( + userId: Long?, + findById: Pair + ): Note? { + if (userId == null) { + return null + } + + if (followerQueryService.alreadyFollow(findById.second.userId, userId)) { + return findById.first + } + return null + } + companion object { private val logger = LoggerFactory.getLogger(NoteApApiServiceImpl::class.java) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt index 422b4874..d69eb9d6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt @@ -77,69 +77,18 @@ class APUserServiceImpl( override suspend fun fetchPerson(url: String, targetActor: String?): Person = fetchPersonWithEntity(url, targetActor).first - @Suppress("LongMethod") override suspend fun fetchPersonWithEntity(url: String, targetActor: String?): Pair { return try { val userEntity = userQueryService.findByUrl(url) val id = userEntity.url - return Person( - type = emptyList(), - name = userEntity.name, - id = id, - preferredUsername = userEntity.name, - summary = userEntity.description, - inbox = "$id/inbox", - outbox = "$id/outbox", - url = id, - icon = Image( - type = emptyList(), - name = "$id/icon.png", - mediaType = "image/png", - url = "$id/icon.png" - ), - publicKey = Key( - type = emptyList(), - name = "Public Key", - id = userEntity.keyId, - owner = id, - publicKeyPem = userEntity.publicKey - ), - endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox"), - followers = userEntity.followers, - following = userEntity.following - ) to userEntity + return entityToPerson(userEntity, id) to userEntity } catch (ignore: FailedToGetResourcesException) { val person = apResourceResolveService.resolve(url, null as Long?) val id = person.id ?: throw IllegalActivityPubObjectException("id is null") try { val userEntity = userQueryService.findByUrl(id) - return Person( - type = emptyList(), - name = userEntity.name, - id = id, - preferredUsername = userEntity.name, - summary = userEntity.description, - inbox = "$id/inbox", - outbox = "$id/outbox", - url = id, - icon = Image( - type = emptyList(), - name = "$id/icon.png", - mediaType = "image/png", - url = "$id/icon.png" - ), - publicKey = Key( - type = emptyList(), - name = "Public Key", - id = userEntity.keyId, - owner = id, - publicKeyPem = userEntity.publicKey - ), - endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox"), - followers = userEntity.followers, - following = userEntity.following - ) to userEntity + return entityToPerson(userEntity, id) to userEntity } catch (_: FailedToGetResourcesException) { } person to userService.createRemoteUser( @@ -163,4 +112,34 @@ class APUserServiceImpl( ) } } + + private fun entityToPerson( + userEntity: User, + id: String + ) = Person( + type = emptyList(), + name = userEntity.name, + id = id, + preferredUsername = userEntity.name, + summary = userEntity.description, + inbox = "$id/inbox", + outbox = "$id/outbox", + url = id, + icon = Image( + type = emptyList(), + name = "$id/icon.png", + mediaType = "image/png", + url = "$id/icon.png" + ), + publicKey = Key( + type = emptyList(), + name = "Public Key", + id = userEntity.keyId, + owner = id, + publicKeyPem = userEntity.publicKey + ), + endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox"), + followers = userEntity.followers, + following = userEntity.following + ) } diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index ecdc3ac9..089a545a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -200,8 +200,8 @@ class SecurityConfig { it.ignoringRequestMatchers(builder.pattern("/inbox")) it.ignoringRequestMatchers(PathRequest.toH2Console()) }.headers { - it.frameOptions { - it.sameOrigin() + it.frameOptions { frameOptionsConfig -> + frameOptionsConfig.sameOrigin() } } return http.build() diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt index be6b3839..4faae800 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt @@ -2,6 +2,7 @@ package dev.usbharu.hideout.core.domain.model.media import dev.usbharu.hideout.core.service.media.FileType import dev.usbharu.hideout.core.service.media.MimeType +import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment data class Media( val id: Long, @@ -14,3 +15,19 @@ data class Media( val blurHash: String?, val description: String? = null ) + +fun Media.toMediaAttachments(): MediaAttachment = MediaAttachment( + id = id.toString(), + type = when (type) { + FileType.Image -> MediaAttachment.Type.image + FileType.Video -> MediaAttachment.Type.video + FileType.Audio -> MediaAttachment.Type.audio + FileType.Unknown -> MediaAttachment.Type.unknown + }, + url = url, + previewUrl = thumbnailUrl, + remoteUrl = remoteUrl, + description = description, + blurhash = blurHash, + textUrl = url +) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/UserRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/UserRepository.kt index 25db9d37..bbc0d346 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/UserRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/UserRepository.kt @@ -2,7 +2,6 @@ package dev.usbharu.hideout.core.domain.model.user import org.springframework.stereotype.Repository -@Suppress("TooManyFunctions") @Repository interface UserRepository { suspend fun save(user: User): User diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt index 8cd3647f..d201fc9a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt @@ -112,7 +112,9 @@ object DeliverRemoveReactionJob : val actor: Prop = string("actor") val like: Prop = string("like") - override fun convert(value: DeliverRemoveReactionJobParam): ScheduleContext.(DeliverRemoveReactionJob) -> Unit = + override fun convert( + value: DeliverRemoveReactionJobParam + ): ScheduleContext.(DeliverRemoveReactionJob) -> Unit = { props[id] = value.id props[inbox] = value.inbox diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt index 8e87edfa..a21fcf4d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt @@ -15,7 +15,7 @@ class PostQueryMapper(private val postResultRowMapper: ResultRowMapper) : .map { it.value } .map { it.first().let(postResultRowMapper::map) - .copy(mediaIds = it.mapNotNull { it.getOrNull(PostsMedia.mediaId) }) + .copy(mediaIds = it.mapNotNull { resultRow -> resultRow.getOrNull(PostsMedia.mediaId) }) } } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueParentService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueParentService.kt index 5bc3ec89..aadf0f9f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueParentService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueParentService.kt @@ -13,7 +13,7 @@ import org.springframework.stereotype.Service @Service @ConditionalOnProperty(name = ["hideout.use-mongodb"], havingValue = "false", matchIfMissing = true) -class KJobJobQueueParentService() : JobQueueParentService { +class KJobJobQueueParentService : JobQueueParentService { private val logger = LoggerFactory.getLogger(this::class.java) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepositoryWrapper.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepositoryWrapper.kt index 1fdbea8e..dfaebfce 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepositoryWrapper.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepositoryWrapper.kt @@ -9,7 +9,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.stereotype.Repository @Repository -@Suppress("InjectDispatcher") @ConditionalOnProperty("hideout.use-mongodb", havingValue = "true", matchIfMissing = false) class MongoTimelineRepositoryWrapper( private val mongoTimelineRepository: MongoTimelineRepository, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt index b18d5ca0..580c4029 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt @@ -197,7 +197,7 @@ class ExposedOAuth2AuthorizationService( } } - @Suppress("LongMethod", "CyclomaticComplexMethod") + @Suppress("LongMethod", "CyclomaticComplexMethod", "CastToNullableType", "UNCHECKED_CAST") fun ResultRow.toAuthorization(): OAuth2Authorization { val registeredClientId = this[Authorization.registeredClientId] @@ -272,7 +272,6 @@ class ExposedOAuth2AuthorizationService( oidcIdTokenValue, oidcTokenIssuedAt, oidcTokenExpiresAt, - @Suppress("CastToNullableType") oidcTokenMetadata.getValue(OAuth2Authorization.Token.CLAIMS_METADATA_NAME) as MutableMap? ) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessService.kt index 2893fa4c..d61d32ef 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessService.kt @@ -74,11 +74,7 @@ class ImageMediaProcessService(private val imageMediaProcessorConfiguration: Ima convertType, it ) - if (write) { - tempThumbnailFile - } else { - null - } + tempThumbnailFile.takeIf { write } } } else { null diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt index 5fc098b2..03d2f49a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt @@ -45,7 +45,7 @@ class ExposedGenerateTimelineService(private val statusQueryService: StatusQuery it[Timelines.postId], it[Timelines.replyId], it[Timelines.repostId], - it[Timelines.mediaIds].split(",").mapNotNull { it.toLongOrNull() } + it[Timelines.mediaIds].split(",").mapNotNull { s -> s.toLongOrNull() } ) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index 6ee4a53c..4bf358b7 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -59,6 +59,7 @@ class UserServiceImpl( } override suspend fun createRemoteUser(user: RemoteUserCreateDto): User { + @Suppress("TooGenericExceptionCaught") val instance = try { instanceService.fetchInstance(user.url, user.sharedInbox) } catch (e: Exception) { diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt index c300375a..5d113667 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt @@ -1,22 +1,19 @@ package dev.usbharu.hideout.mastodon.infrastructure.exposedquery +import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments import dev.usbharu.hideout.core.infrastructure.exposedrepository.* -import dev.usbharu.hideout.core.service.media.FileType import dev.usbharu.hideout.domain.mastodon.model.generated.Account -import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment import dev.usbharu.hideout.domain.mastodon.model.generated.Status import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery import dev.usbharu.hideout.mastodon.query.StatusQueryService import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.innerJoin import org.jetbrains.exposed.sql.select import org.springframework.stereotype.Repository import java.time.Instant @Repository class StatusQueryServiceImpl : StatusQueryService { - @Suppress("LongMethod") - override suspend fun findByPostIds(ids: List): List = findByPostIdsWithMediaAttachments(ids) + override suspend fun findByPostIds(ids: List): List = findByPostIdsWithMedia(ids) override suspend fun findByPostIdsWithMediaIds(statusQueries: List): List { val postIdSet = mutableSetOf() @@ -29,23 +26,7 @@ class StatusQueryServiceImpl : StatusQueryService { .associate { it[Posts.id] to toStatus(it) } val mediaMap = Media.select { Media.id inList mediaIdSet } .associate { - it[Media.id] to it.toMedia().let { - MediaAttachment( - id = it.id.toString(), - type = when (it.type) { - FileType.Image -> MediaAttachment.Type.image - FileType.Video -> MediaAttachment.Type.video - FileType.Audio -> MediaAttachment.Type.audio - FileType.Unknown -> MediaAttachment.Type.unknown - }, - url = it.url, - previewUrl = it.thumbnailUrl, - remoteUrl = it.remoteUrl, - description = "", - blurhash = it.blurHash, - textUrl = it.url - ) - } + it[Media.id] to it.toMedia().toMediaAttachments() } return statusQueries.mapNotNull { statusQuery -> @@ -58,18 +39,6 @@ class StatusQueryServiceImpl : StatusQueryService { } } - @Suppress("unused") - private suspend fun internalFindByPostIds(ids: List): List { - val pairs = Posts - .innerJoin(Users, onColumn = { Posts.userId }, otherColumn = { Users.id }) - .select { Posts.id inList ids } - .map { - toStatus(it) to it[Posts.repostId] - } - - return resolveReplyAndRepost(pairs) - } - private fun resolveReplyAndRepost(pairs: List>): List { val statuses = pairs.map { it.first } return pairs @@ -89,8 +58,7 @@ class StatusQueryServiceImpl : StatusQueryService { } } - @Suppress("FunctionMaxLength") - private suspend fun findByPostIdsWithMediaAttachments(ids: List): List { + private suspend fun findByPostIdsWithMedia(ids: List): List { val pairs = Posts .leftJoin(PostsMedia) .leftJoin(Users) @@ -100,24 +68,8 @@ class StatusQueryServiceImpl : StatusQueryService { .map { it.value } .map { toStatus(it.first()).copy( - mediaAttachments = it.mapNotNull { - it.toMediaOrNull()?.let { - MediaAttachment( - id = it.id.toString(), - type = when (it.type) { - FileType.Image -> MediaAttachment.Type.image - FileType.Video -> MediaAttachment.Type.video - FileType.Audio -> MediaAttachment.Type.audio - FileType.Unknown -> MediaAttachment.Type.unknown - }, - url = it.url, - previewUrl = it.thumbnailUrl, - remoteUrl = it.remoteUrl, - description = "", - blurhash = it.blurHash, - textUrl = it.url - ) - } + mediaAttachments = it.mapNotNull { resultRow -> + resultRow.toMediaOrNull()?.toMediaAttachments() } ) to it.first()[Posts.repostId] } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt index 715a6819..98803f6b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt @@ -1,7 +1,10 @@ package dev.usbharu.hideout.mastodon.interfaces.api.status import com.fasterxml.jackson.annotation.JsonProperty +import dev.usbharu.hideout.core.domain.model.post.Visibility +import dev.usbharu.hideout.domain.mastodon.model.generated.Status import dev.usbharu.hideout.domain.mastodon.model.generated.StatusesRequestPoll +import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusesRequest.Visibility.* @Suppress("VariableNaming", "EnumEntryName") class StatusesRequest { @@ -67,7 +70,7 @@ class StatusesRequest { " scheduledAt=$scheduled_at)" } - @Suppress("EnumNaming") + @Suppress("EnumNaming", "EnumEntryNameCase") enum class Visibility { `public`, unlisted, @@ -75,3 +78,23 @@ class StatusesRequest { direct } } + +fun StatusesRequest.Visibility?.toPostVisibility(): Visibility { + return when (this) { + public -> Visibility.PUBLIC + unlisted -> Visibility.UNLISTED + private -> Visibility.FOLLOWERS + direct -> Visibility.DIRECT + null -> Visibility.PUBLIC + } +} + +fun StatusesRequest.Visibility?.toStatusVisibility(): Status.Visibility { + return when (this) { + public -> Status.Visibility.public + unlisted -> Status.Visibility.unlisted + private -> Status.Visibility.private + direct -> Status.Visibility.direct + null -> Status.Visibility.public + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt index 1cc448e0..10b1a0c5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt @@ -3,15 +3,15 @@ package dev.usbharu.hideout.mastodon.service.status import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.media.MediaRepository -import dev.usbharu.hideout.core.domain.model.post.Visibility +import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments import dev.usbharu.hideout.core.query.PostQueryService import dev.usbharu.hideout.core.query.UserQueryService -import dev.usbharu.hideout.core.service.media.FileType import dev.usbharu.hideout.core.service.post.PostCreateDto import dev.usbharu.hideout.core.service.post.PostService -import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment import dev.usbharu.hideout.domain.mastodon.model.generated.Status import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusesRequest +import dev.usbharu.hideout.mastodon.interfaces.api.status.toPostVisibility +import dev.usbharu.hideout.mastodon.interfaces.api.status.toStatusVisibility import dev.usbharu.hideout.mastodon.service.account.AccountService import org.springframework.stereotype.Service import java.time.Instant @@ -34,24 +34,15 @@ class StatsesApiServiceImpl( private val transaction: Transaction ) : StatusesApiService { - @Suppress("LongMethod", "CyclomaticComplexMethod") override suspend fun postStatus( statusesRequest: StatusesRequest, userId: Long ): Status = transaction.transaction { - val visibility = when (statusesRequest.visibility) { - StatusesRequest.Visibility.public -> Visibility.PUBLIC - StatusesRequest.Visibility.unlisted -> Visibility.UNLISTED - StatusesRequest.Visibility.private -> Visibility.FOLLOWERS - StatusesRequest.Visibility.direct -> Visibility.DIRECT - null -> Visibility.PUBLIC - } - val post = postService.createLocal( PostCreateDto( text = statusesRequest.status.orEmpty(), overview = statusesRequest.spoiler_text, - visibility = visibility, + visibility = statusesRequest.visibility.toPostVisibility(), repolyId = statusesRequest.in_reply_to_id?.toLongOrNull(), userId = userId, mediaIds = statusesRequest.media_ids.map { it.toLong() } @@ -59,14 +50,6 @@ class StatsesApiServiceImpl( ) val account = accountService.findById(userId) - val postVisibility = when (statusesRequest.visibility) { - StatusesRequest.Visibility.public -> Status.Visibility.public - StatusesRequest.Visibility.unlisted -> Status.Visibility.unlisted - StatusesRequest.Visibility.private -> Status.Visibility.private - StatusesRequest.Visibility.direct -> Status.Visibility.direct - null -> Status.Visibility.public - } - val replyUser = if (post.replyId != null) { try { userQueryService.findById(postQueryService.findById(post.replyId).userId).id @@ -81,21 +64,7 @@ class StatsesApiServiceImpl( val mediaAttachment = post.mediaIds.map { mediaId -> mediaRepository.findById(mediaId) }.map { - MediaAttachment( - id = it.id.toString(), - type = when (it.type) { - FileType.Image -> MediaAttachment.Type.image - FileType.Video -> MediaAttachment.Type.video - FileType.Audio -> MediaAttachment.Type.audio - FileType.Unknown -> MediaAttachment.Type.unknown - }, - url = it.url, - previewUrl = it.thumbnailUrl, - remoteUrl = it.remoteUrl, - description = "", - blurhash = it.blurHash, - textUrl = it.url - ) + it.toMediaAttachments() } Status( @@ -104,7 +73,7 @@ class StatsesApiServiceImpl( createdAt = Instant.ofEpochMilli(post.createdAt).toString(), account = account, content = post.text, - visibility = postVisibility, + visibility = statusesRequest.visibility.toStatusVisibility(), sensitive = post.sensitive, spoilerText = post.overview.orEmpty(), mediaAttachments = mediaAttachment, From 6c2d5dae94a52306f2212e3b4258b4177c820981 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 Nov 2023 10:44:54 +0900 Subject: [PATCH 035/116] =?UTF-8?q?refactor:=20=E4=B8=80=E9=83=A8=E3=81=AE?= =?UTF-8?q?AP=E3=81=AEJSON=E3=83=9E=E3=83=83=E3=83=94=E3=83=B3=E3=82=B0?= =?UTF-8?q?=E7=94=A8=E3=81=AEPOJO=E3=82=92Null-safe=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usbharu/hideout/activitypub/domain/model/Note.kt | 12 ++++++------ .../service/objects/note/APNoteService.kt | 5 ++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt index ddca1d67..cc891c08 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt @@ -3,10 +3,10 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object open class Note : Object { - var attributedTo: String? = null + lateinit var attributedTo: String var attachment: List = emptyList() - var content: String? = null - var published: String? = null + lateinit var content: String + lateinit var published: String var to: List = emptyList() var cc: List = emptyList() var sensitive: Boolean = false @@ -19,9 +19,9 @@ open class Note : Object { type: List = emptyList(), name: String, id: String?, - attributedTo: String?, - content: String?, - published: String?, + attributedTo: String, + content: String, + published: String, to: List = emptyList(), cc: List = emptyList(), sensitive: Boolean = false, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt index f7300314..28bf7c01 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt @@ -1,7 +1,6 @@ package dev.usbharu.hideout.activitypub.service.objects.note import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException import dev.usbharu.hideout.activitypub.domain.model.Note import dev.usbharu.hideout.activitypub.query.NoteQueryService import dev.usbharu.hideout.activitypub.service.common.APResourceResolveService @@ -99,7 +98,7 @@ class APNoteServiceImpl( private suspend fun saveNote(note: Note, targetActor: String?, url: String): Note { val person = apUserService.fetchPersonWithEntity( - note.attributedTo ?: throw IllegalActivityPubObjectException("note.attributedTo is null"), + note.attributedTo, targetActor ) @@ -142,7 +141,7 @@ class APNoteServiceImpl( postBuilder.of( id = postRepository.generateId(), userId = person.second.id, - text = note.content.orEmpty(), + text = note.content, createdAt = Instant.parse(note.published).toEpochMilli(), visibility = visibility, url = note.id ?: url, From 7a34b11147a18974422c3919f41371fc52d52fdc Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:19:27 +0900 Subject: [PATCH 036/116] =?UTF-8?q?refactor:=20Object=E3=82=92=E7=B6=99?= =?UTF-8?q?=E6=89=BF=E3=81=99=E3=82=8BJSON=E3=83=9E=E3=83=83=E3=83=94?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E7=94=A8=E3=81=AEPOJO=E3=82=92Null-safe?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/domain/model/Accept.kt | 45 ++++++----- .../activitypub/domain/model/Create.kt | 57 +++++++------- .../activitypub/domain/model/Delete.kt | 27 +++++-- .../activitypub/domain/model/Document.kt | 20 ++--- .../hideout/activitypub/domain/model/Emoji.kt | 5 +- .../activitypub/domain/model/Follow.kt | 25 +++--- .../activitypub/domain/model/HasActor.kt | 5 ++ .../hideout/activitypub/domain/model/HasId.kt | 5 ++ .../activitypub/domain/model/HasName.kt | 5 ++ .../hideout/activitypub/domain/model/Image.kt | 15 ++-- .../hideout/activitypub/domain/model/Key.kt | 22 +++--- .../hideout/activitypub/domain/model/Like.kt | 33 +++++--- .../hideout/activitypub/domain/model/Note.kt | 77 +++++++------------ .../activitypub/domain/model/Person.kt | 58 ++++---------- .../activitypub/domain/model/Tombstone.kt | 27 +++++-- .../hideout/activitypub/domain/model/Undo.kt | 28 +++++-- .../domain/model/objects/Object.kt | 22 ++---- .../domain/model/objects/ObjectValue.kt | 13 ++-- 18 files changed, 258 insertions(+), 231 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasActor.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasId.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasName.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt index c249afb4..d1b0936b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt @@ -1,41 +1,46 @@ package dev.usbharu.hideout.activitypub.domain.model +import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer -open class Accept : Object { +open class Accept @JsonCreator constructor( + type: List = emptyList(), + override val name: String, @JsonDeserialize(using = ObjectDeserializer::class) @Suppress("VariableNaming") - var `object`: Object? = null - - protected constructor() - constructor( - type: List = emptyList(), - name: String, - `object`: Object?, - actor: String? - ) : super( - type = add(type, "Accept"), - name = name, - actor = actor - ) { - this.`object` = `object` - } - - override fun toString(): String = "Accept(`object`=$`object`) ${super.toString()}" + var `object`: Object?, + override val actor: String +) : Object( + type = add(type, "Accept") +), + HasActor, + HasName { override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Accept) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false - return `object` == other.`object` + other as Accept + + if (`object` != other.`object`) return false + if (actor != other.actor) return false + if (name != other.name) return false + + return true } override fun hashCode(): Int { var result = super.hashCode() result = 31 * result + (`object`?.hashCode() ?: 0) + result = 31 * result + actor.hashCode() + result = 31 * result + name.hashCode() return result } + + override fun toString(): String { + return "Accept(" + "`object`=$`object`, " + "actor='$actor', " + "name='$name'" + ")" + " ${super.toString()}" + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt index 6b19ab5b..df22045c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt @@ -4,46 +4,51 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer -open class Create : Object { +open class Create( + type: List = emptyList(), + override val name: String, @JsonDeserialize(using = ObjectDeserializer::class) @Suppress("VariableNaming") - var `object`: Object? = null - var to: List = emptyList() + var `object`: Object?, + override val actor: String, + override val id: String, + var to: List = emptyList(), var cc: List = emptyList() - - protected constructor() : super() - constructor( - type: List = emptyList(), - name: String? = null, - `object`: Object?, - actor: String? = null, - id: String? = null, - to: List = emptyList(), - cc: List = emptyList() - ) : super( - type = add(type, "Create"), - name = name, - actor = actor, - id = id - ) { - this.`object` = `object` - this.to = to - this.cc = cc - } +) : Object( + type = add(type, "Create") +), + HasId, + HasName, + HasActor { override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Create) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false - return `object` == other.`object` + other as Create + + if (`object` != other.`object`) return false + if (to != other.to) return false + if (cc != other.cc) return false + if (name != other.name) return false + if (actor != other.actor) return false + if (id != other.id) return false + + return true } override fun hashCode(): Int { var result = super.hashCode() result = 31 * result + (`object`?.hashCode() ?: 0) + result = 31 * result + to.hashCode() + result = 31 * result + cc.hashCode() + result = 31 * result + name.hashCode() + result = 31 * result + actor.hashCode() + result = 31 * result + id.hashCode() return result } - override fun toString(): String = "Create(`object`=$`object`) ${super.toString()}" + override fun toString(): String = + "Create(`object`=$`object`, to=$to, cc=$cc, name='$name', actor='$actor', id='$id') ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt index e29e9a93..5b867818 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt @@ -4,33 +4,42 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer -open class Delete : Object { +open class Delete : Object, HasId, HasActor, HasName { @JsonDeserialize(using = ObjectDeserializer::class) @Suppress("VariableNaming") var `object`: Object? = null var published: String? = null + override val actor: String + override val id: String + override val name: String constructor( type: List = emptyList(), - name: String? = "Delete", + name: String = "Delete", actor: String, id: String, `object`: Object, published: String? - ) : super(add(type, "Delete"), name, actor, id) { + ) : super(add(type, "Delete")) { this.`object` = `object` this.published = published + this.name = name + this.actor = actor + this.id = id } - protected constructor() : super() - override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Delete) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false + other as Delete + if (`object` != other.`object`) return false if (published != other.published) return false + if (actor != other.actor) return false + if (id != other.id) return false + if (name != other.name) return false return true } @@ -39,8 +48,12 @@ open class Delete : Object { var result = super.hashCode() result = 31 * result + (`object`?.hashCode() ?: 0) result = 31 * result + (published?.hashCode() ?: 0) + result = 31 * result + actor.hashCode() + result = 31 * result + id.hashCode() + result = 31 * result + name.hashCode() return result } - override fun toString(): String = "Delete(`object`=$`object`, published=$published) ${super.toString()}" + override fun toString(): String = + "Delete(`object`=$`object`, published=$published, actor='$actor', id='$id', name='$name') ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt index d4f70180..489029d0 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt @@ -2,34 +2,35 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object -open class Document : Object { +open class Document : Object, HasName { var mediaType: String? = null var url: String? = null + override val name: String - protected constructor() : super() constructor( type: List = emptyList(), - name: String? = null, + name: String, mediaType: String, url: String ) : super( - type = add(type, "Document"), - name = name, - actor = null, - id = null + type = add(type, "Document") ) { this.mediaType = mediaType this.url = url + this.name = name } override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Document) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false + other as Document + if (mediaType != other.mediaType) return false if (url != other.url) return false + if (name != other.name) return false return true } @@ -38,8 +39,9 @@ open class Document : Object { var result = super.hashCode() result = 31 * result + (mediaType?.hashCode() ?: 0) result = 31 * result + (url?.hashCode() ?: 0) + result = 31 * result + name.hashCode() return result } - override fun toString(): String = "Document(mediaType=$mediaType, url=$url) ${super.toString()}" + override fun toString(): String = "Document(mediaType=$mediaType, url=$url, name='$name') ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt index cce3ee87..c270be48 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt @@ -15,10 +15,7 @@ open class Emoji : Object { updated: String?, icon: Image? ) : super( - type = add(type, "Emoji"), - name = name, - actor = actor, - id = id + type = add(type, "Emoji") ) { this.updated = updated this.icon = icon diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt index 00e3eec0..73ff75f3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt @@ -2,37 +2,42 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object -open class Follow : Object { +open class Follow : Object, HasActor { @Suppress("VariableNaming") var `object`: String? = null - protected constructor() : super() + override val actor: String + constructor( type: List = emptyList(), - name: String?, `object`: String?, - actor: String? + actor: String ) : super( - type = add(type, "Follow"), - name = name, - actor = actor + type = add(type, "Follow") ) { this.`object` = `object` + this.actor = actor } override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Follow) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false - return `object` == other.`object` + other as Follow + + if (`object` != other.`object`) return false + if (actor != other.actor) return false + + return true } override fun hashCode(): Int { var result = super.hashCode() result = 31 * result + (`object`?.hashCode() ?: 0) + result = 31 * result + actor.hashCode() return result } - override fun toString(): String = "Follow(`object`=$`object`) ${super.toString()}" + override fun toString(): String = "Follow(`object`=$`object`, actor='$actor') ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasActor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasActor.kt new file mode 100644 index 00000000..c9bc4f91 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasActor.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.activitypub.domain.model + +interface HasActor { + val actor: String +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasId.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasId.kt new file mode 100644 index 00000000..774032c8 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasId.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.activitypub.domain.model + +interface HasId { + val id: String +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasName.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasName.kt new file mode 100644 index 00000000..b8e4de76 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasName.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.activitypub.domain.model + +interface HasName { + val name: String +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt index f177c8a0..60b0c8ac 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt @@ -7,9 +7,8 @@ open class Image : Object { private var url: String? = null protected constructor() : super() - constructor(type: List = emptyList(), name: String, mediaType: String?, url: String?) : super( - add(type, "Image"), - name + constructor(type: List = emptyList(), mediaType: String?, url: String?) : super( + add(type, "Image") ) { this.mediaType = mediaType this.url = url @@ -17,11 +16,15 @@ open class Image : Object { override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Image) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false + other as Image + if (mediaType != other.mediaType) return false - return url == other.url + if (url != other.url) return false + + return true } override fun hashCode(): Int { @@ -30,4 +33,6 @@ open class Image : Object { result = 31 * result + (url?.hashCode() ?: 0) return result } + + override fun toString(): String = "Image(mediaType=$mediaType, url=$url) ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt index 5cc33766..993d4af9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt @@ -2,41 +2,45 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object -open class Key : Object { +open class Key : Object, HasId { var owner: String? = null var publicKeyPem: String? = null + override val id: String - protected constructor() : super() constructor( type: List, - name: String, id: String, owner: String?, publicKeyPem: String? ) : super( - type = add(list = type, type = "Key"), - name = name, - id = id + type = add(list = type, type = "Key") ) { this.owner = owner this.publicKeyPem = publicKeyPem + this.id = id } override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Key) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false + other as Key + if (owner != other.owner) return false - return publicKeyPem == other.publicKeyPem + if (publicKeyPem != other.publicKeyPem) return false + if (id != other.id) return false + + return true } override fun hashCode(): Int { var result = super.hashCode() result = 31 * result + (owner?.hashCode() ?: 0) result = 31 * result + (publicKeyPem?.hashCode() ?: 0) + result = 31 * result + id.hashCode() return result } - override fun toString(): String = "Key(owner=$owner, publicKeyPem=$publicKeyPem) ${super.toString()}" + override fun toString(): String = "Key(owner=$owner, publicKeyPem=$publicKeyPem, id='$id') ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt index c916f566..2400eeff 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt @@ -4,42 +4,47 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer -open class Like : Object { +open class Like : Object, HasId, HasActor { @Suppress("VariableNaming") var `object`: String? = null var content: String? = null @JsonDeserialize(contentUsing = ObjectDeserializer::class) var tag: List = emptyList() + override val actor: String + override val id: String - protected constructor() : super() constructor( type: List = emptyList(), - name: String?, - actor: String?, - id: String?, + actor: String, + id: String, `object`: String?, content: String?, tag: List = emptyList() ) : super( - type = add(type, "Like"), - name = name, - actor = actor, - id = id + type = add(type, "Like") ) { this.`object` = `object` this.content = content this.tag = tag + this.actor = actor + this.id = id } override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Like) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false + other as Like + if (`object` != other.`object`) return false if (content != other.content) return false - return tag == other.tag + if (tag != other.tag) return false + if (actor != other.actor) return false + if (id != other.id) return false + + return true } override fun hashCode(): Int { @@ -47,8 +52,12 @@ open class Like : Object { result = 31 * result + (`object`?.hashCode() ?: 0) result = 31 * result + (content?.hashCode() ?: 0) result = 31 * result + tag.hashCode() + result = 31 * result + actor.hashCode() + result = 31 * result + id.hashCode() return result } - override fun toString(): String = "Like(`object`=$`object`, content=$content, tag=$tag) ${super.toString()}" + override fun toString(): String { + return "Like(`object`=$`object`, content=$content, tag=$tag, actor='$actor', id='$id') ${super.toString()}" + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt index cc891c08..74d5ce7a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt @@ -2,79 +2,58 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object -open class Note : Object { - lateinit var attributedTo: String +open class Note +@Suppress("LongParameterList") +constructor( + type: List = emptyList(), + override val id: String, + var attributedTo: String, + var content: String, + var published: String, + var to: List = emptyList(), + var cc: List = emptyList(), + var sensitive: Boolean = false, + var inReplyTo: String? = null, var attachment: List = emptyList() - lateinit var content: String - lateinit var published: String - var to: List = emptyList() - var cc: List = emptyList() - var sensitive: Boolean = false - var inReplyTo: String? = null - - protected constructor() : super() - - @Suppress("LongParameterList") - constructor( - type: List = emptyList(), - name: String, - id: String?, - attributedTo: String, - content: String, - published: String, - to: List = emptyList(), - cc: List = emptyList(), - sensitive: Boolean = false, - inReplyTo: String? = null, - attachment: List = emptyList() - ) : super( - type = add(type, "Note"), - name = name, - id = id - ) { - this.attributedTo = attributedTo - this.content = content - this.published = published - this.to = to - this.cc = cc - this.sensitive = sensitive - this.inReplyTo = inReplyTo - this.attachment = attachment - } +) : Object( + type = add(type, "Note") +), + HasId { override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Note) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false + other as Note + + if (id != other.id) return false if (attributedTo != other.attributedTo) return false - if (attachment != other.attachment) return false if (content != other.content) return false if (published != other.published) return false if (to != other.to) return false if (cc != other.cc) return false if (sensitive != other.sensitive) return false if (inReplyTo != other.inReplyTo) return false + if (attachment != other.attachment) return false return true } override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (attributedTo?.hashCode() ?: 0) - result = 31 * result + attachment.hashCode() - result = 31 * result + (content?.hashCode() ?: 0) - result = 31 * result + (published?.hashCode() ?: 0) + result = 31 * result + id.hashCode() + result = 31 * result + attributedTo.hashCode() + result = 31 * result + content.hashCode() + result = 31 * result + published.hashCode() result = 31 * result + to.hashCode() result = 31 * result + cc.hashCode() result = 31 * result + sensitive.hashCode() result = 31 * result + (inReplyTo?.hashCode() ?: 0) + result = 31 * result + attachment.hashCode() return result } - override fun toString(): String { - return "Note(attributedTo=$attributedTo, attachment=$attachment, " + - "content=$content, published=$published, to=$to, cc=$cc, sensitive=$sensitive," + - " inReplyTo=$inReplyTo) ${super.toString()}" - } + override fun toString(): String = + "Note(id='$id', attributedTo='$attributedTo', content='$content', published='$published', to=$to, cc=$cc, sensitive=$sensitive, inReplyTo=$inReplyTo, attachment=$attachment) ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt index 7ee0075e..e94cc9b1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt @@ -2,47 +2,23 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object -open class Person : Object { - var preferredUsername: String? = null - var summary: String? = null - var inbox: String? = null - var outbox: String? = null - var url: String? = null - private var icon: Image? = null - var publicKey: Key? = null - var endpoints: Map = emptyMap() - var following: String? = null - var followers: String? = null - - protected constructor() : super() - - @Suppress("LongParameterList") - constructor( - type: List = emptyList(), - name: String, - id: String?, - preferredUsername: String?, - summary: String?, - inbox: String?, - outbox: String?, - url: String?, - icon: Image?, - publicKey: Key?, - endpoints: Map = emptyMap(), - followers: String?, - following: String? - ) : super(add(type, "Person"), name, id = id) { - this.preferredUsername = preferredUsername - this.summary = summary - this.inbox = inbox - this.outbox = outbox - this.url = url - this.icon = icon - this.publicKey = publicKey - this.endpoints = endpoints - this.followers = followers - this.following = following - } +open class Person +@Suppress("LongParameterList") +constructor( + type: List = emptyList(), + override val name: String, + override val id: String, + var preferredUsername: String?, + var summary: String?, + var inbox: String?, + var outbox: String?, + var url: String?, + private var icon: Image?, + var publicKey: Key?, + var endpoints: Map = emptyMap(), + var followers: String?, + var following: String? +) : Object(add(type, "Person")), HasId, HasName { override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Tombstone.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Tombstone.kt index 0017eac4..201eba32 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Tombstone.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Tombstone.kt @@ -2,11 +2,24 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object -open class Tombstone : Object { - constructor( - type: List = emptyList(), - name: String = "Tombstone", - actor: String? = null, - id: String - ) : super(add(type, "Tombstone"), name, actor, id) +open class Tombstone(type: List = emptyList(), override val id: String) : + Object(add(type, "Tombstone")), + HasId { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + if (!super.equals(other)) return false + + other as Tombstone + + return id == other.id + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + id.hashCode() + return result + } + + override fun toString(): String = "Tombstone(id='$id') ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt index a8fbb65a..f1a0d9d6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt @@ -5,41 +5,53 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer import java.time.Instant -open class Undo : Object { +open class Undo : Object, HasId, HasActor { @JsonDeserialize(using = ObjectDeserializer::class) @Suppress("VariableNaming") var `object`: Object? = null var published: String? = null + override val actor: String + override val id: String - protected constructor() : super() constructor( type: List = emptyList(), - name: String, actor: String, - id: String?, + id: String, `object`: Object, published: Instant - ) : super(add(type, "Undo"), name, actor, id) { + ) : super(add(type, "Undo")) { this.`object` = `object` this.published = published.toString() + this.id = id + this.actor = actor } override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Undo) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false + other as Undo + if (`object` != other.`object`) return false - return published == other.published + if (published != other.published) return false + if (actor != other.actor) return false + if (id != other.id) return false + + return true } override fun hashCode(): Int { var result = super.hashCode() result = 31 * result + (`object`?.hashCode() ?: 0) result = 31 * result + (published?.hashCode() ?: 0) + result = 31 * result + actor.hashCode() + result = 31 * result + id.hashCode() return result } - override fun toString(): String = "Undo(`object`=$`object`, published=$published) ${super.toString()}" + override fun toString(): String { + return "Undo(`object`=$`object`, published=$published, actor='$actor', id='$id') ${super.toString()}" + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt index 23f26eac..cafdb44d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt @@ -12,41 +12,29 @@ open class Object : JsonLd { set(value) { field = value.filter { it.isNotBlank() } } - var name: String? = null - var actor: String? = null - var id: String? = null protected constructor() - constructor(type: List, name: String? = null, actor: String? = null, id: String? = null) : super() { + constructor(type: List) : super() { this.type = type.filter { it.isNotBlank() } - this.name = name - this.actor = actor - this.id = id } override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Object) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false - if (type != other.type) return false - if (name != other.name) return false - if (actor != other.actor) return false - if (id != other.id) return false + other as Object - return true + return type == other.type } override fun hashCode(): Int { var result = super.hashCode() result = 31 * result + type.hashCode() - result = 31 * result + (name?.hashCode() ?: 0) - result = 31 * result + (actor?.hashCode() ?: 0) - result = 31 * result + (id?.hashCode() ?: 0) return result } - override fun toString(): String = "Object(type=$type, name=$name, actor=$actor, id=$id) ${super.toString()}" + override fun toString(): String = "Object(type=$type) ${super.toString()}" companion object { @JvmStatic diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt index b97b2541..2a9c8eef 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt @@ -1,16 +1,15 @@ package dev.usbharu.hideout.activitypub.domain.model.objects +import com.fasterxml.jackson.annotation.JsonCreator + @Suppress("VariableNaming") open class ObjectValue : Object { - var `object`: String? = null + lateinit var `object`: String - protected constructor() : super() - constructor(type: List, name: String?, actor: String?, id: String?, `object`: String?) : super( - type, - name, - actor, - id + @JsonCreator + constructor(type: List) : super( + type ) { this.`object` = `object` } From 2e1cee4e1af23a8fd9775878c49241f6ce646993 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:58:30 +0900 Subject: [PATCH 037/116] =?UTF-8?q?refactor:=20POJO=E3=81=AE=E5=A4=89?= =?UTF-8?q?=E6=9B=B4=E3=82=92=E5=8F=8D=E6=98=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/domain/model/Delete.kt | 9 ++---- .../activitypub/domain/model/Document.kt | 28 +++++++----------- .../hideout/activitypub/domain/model/Emoji.kt | 29 +++++++------------ .../hideout/activitypub/domain/model/Undo.kt | 5 ++-- .../model/objects/ObjectDeserializer.kt | 3 -- .../domain/model/objects/ObjectValue.kt | 21 +++++--------- .../exposedquery/NoteQueryServiceImpl.kt | 1 - .../activity/delete/APDeleteProcessor.kt | 7 ++++- .../activity/follow/APSendFollowService.kt | 1 - .../activity/like/ApReactionJobProcessor.kt | 1 - .../like/ApRemoveReactionJobProcessor.kt | 3 +- .../service/objects/user/APUserService.kt | 4 --- .../application/config/ActivityPubConfig.kt | 1 + .../domain/model/DeleteSerializeTest.kt | 4 +-- .../domain/model/NoteSerializeTest.kt | 5 +--- .../activitypub/domain/model/UndoTest.kt | 10 +++---- .../model/objects/ObjectSerializeTest.kt | 15 ++-------- .../api/actor/UserAPControllerImplTest.kt | 2 -- .../api/note/NoteApControllerImplTest.kt | 2 -- .../create/ApSendCreateServiceImplTest.kt | 1 - .../follow/APSendFollowServiceImplTest.kt | 1 - .../common/APRequestServiceImplTest.kt | 14 ++------- .../objects/note/APNoteServiceImplTest.kt | 11 ++----- .../hideout/ap/ContextSerializerTest.kt | 1 - 24 files changed, 56 insertions(+), 123 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt index 5b867818..10b919c5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt @@ -4,18 +4,16 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer -open class Delete : Object, HasId, HasActor, HasName { +open class Delete : Object, HasId, HasActor { @JsonDeserialize(using = ObjectDeserializer::class) @Suppress("VariableNaming") var `object`: Object? = null var published: String? = null override val actor: String override val id: String - override val name: String constructor( type: List = emptyList(), - name: String = "Delete", actor: String, id: String, `object`: Object, @@ -23,7 +21,6 @@ open class Delete : Object, HasId, HasActor, HasName { ) : super(add(type, "Delete")) { this.`object` = `object` this.published = published - this.name = name this.actor = actor this.id = id } @@ -39,7 +36,6 @@ open class Delete : Object, HasId, HasActor, HasName { if (published != other.published) return false if (actor != other.actor) return false if (id != other.id) return false - if (name != other.name) return false return true } @@ -50,10 +46,9 @@ open class Delete : Object, HasId, HasActor, HasName { result = 31 * result + (published?.hashCode() ?: 0) result = 31 * result + actor.hashCode() result = 31 * result + id.hashCode() - result = 31 * result + name.hashCode() return result } override fun toString(): String = - "Delete(`object`=$`object`, published=$published, actor='$actor', id='$id', name='$name') ${super.toString()}" + "Delete(`object`=$`object`, published=$published, actor='$actor', id='$id') ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt index 489029d0..480c6011 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt @@ -2,24 +2,18 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object -open class Document : Object, HasName { +open class Document( + type: List = emptyList(), + override val name: String = "", + mediaType: String, + url: String +) : Object( + type = add(type, "Document") +), + HasName { - var mediaType: String? = null - var url: String? = null - override val name: String - - constructor( - type: List = emptyList(), - name: String, - mediaType: String, - url: String - ) : super( - type = add(type, "Document") - ) { - this.mediaType = mediaType - this.url = url - this.name = name - } + var mediaType: String? = mediaType + var url: String? = url override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt index c270be48..0a91be60 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt @@ -2,24 +2,17 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object -open class Emoji : Object { - var updated: String? = null - var icon: Image? = null - - protected constructor() : super() - constructor( - type: List, - name: String?, - actor: String?, - id: String?, - updated: String?, - icon: Image? - ) : super( - type = add(type, "Emoji") - ) { - this.updated = updated - this.icon = icon - } +open class Emoji( + type: List, + override val name: String, + override val id: String, + var updated: String?, + var icon: Image? +) : Object( + type = add(type, "Emoji") +), + HasName, + HasId { override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt index f1a0d9d6..857416ef 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt @@ -3,7 +3,6 @@ package dev.usbharu.hideout.activitypub.domain.model import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer -import java.time.Instant open class Undo : Object, HasId, HasActor { @@ -19,10 +18,10 @@ open class Undo : Object, HasId, HasActor { actor: String, id: String, `object`: Object, - published: Instant + published: String ) : super(add(type, "Undo")) { this.`object` = `object` - this.published = published.toString() + this.published = published this.id = id this.actor = actor } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt index acbf32e5..f28070e6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt @@ -15,9 +15,6 @@ class ObjectDeserializer : JsonDeserializer() { if (treeNode.isValueNode) { return ObjectValue( emptyList(), - null, - null, - null, treeNode.asText() ) } else if (treeNode.isObject) { diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt index 2a9c8eef..62ed4344 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt @@ -3,30 +3,25 @@ package dev.usbharu.hideout.activitypub.domain.model.objects import com.fasterxml.jackson.annotation.JsonCreator @Suppress("VariableNaming") -open class ObjectValue : Object { - - lateinit var `object`: String - - @JsonCreator - constructor(type: List) : super( - type - ) { - this.`object` = `object` - } +open class ObjectValue @JsonCreator constructor(type: List, var `object`: String) : Object( + type +) { override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is ObjectValue) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false + other as ObjectValue + return `object` == other.`object` } override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (`object`?.hashCode() ?: 0) + result = 31 * result + `object`.hashCode() return result } - override fun toString(): String = "ObjectValue(`object`=$`object`) ${super.toString()}" + override fun toString(): String = "ObjectValue(`object`='$`object`') ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt index 3348f832..561b8de2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt @@ -64,7 +64,6 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v this[Users.followers] ) return Note( - name = "Post", id = this[Posts.apId], attributedTo = this[Users.url], content = this[Posts.text], diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt index 060dc713..f6f136fa 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt @@ -2,6 +2,7 @@ package dev.usbharu.hideout.activitypub.service.activity.delete import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException import dev.usbharu.hideout.activitypub.domain.model.Delete +import dev.usbharu.hideout.activitypub.domain.model.HasId import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType @@ -17,7 +18,11 @@ class APDeleteProcessor( ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val deleteId = activity.activity.`object`?.id ?: throw IllegalActivityPubObjectException("object.id is null") + val value = activity.activity.`object` + if (value !is HasId) { + throw IllegalActivityPubObjectException("object hasn't id") + } + val deleteId = value.id val post = try { postQueryService.findByApId(deleteId) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt index e71e7c79..554fa571 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt @@ -15,7 +15,6 @@ class APSendFollowServiceImpl( ) : APSendFollowService { override suspend fun sendFollow(sendFollowDto: SendFollowDto) { val follow = Follow( - name = "Follow", `object` = sendFollowDto.followTargetUserId.url, actor = sendFollowDto.userId.url ) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt index af3f2f09..3f73bb2e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt @@ -21,7 +21,6 @@ class ApReactionJobProcessor( apRequestService.apPost( param.inbox, Like( - name = "Like", actor = param.actor, `object` = param.postUrl, id = "${applicationConfig.url}/liek/note/${param.id}", diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt index 307f0c16..6a873aca 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt @@ -28,11 +28,10 @@ class ApRemoveReactionJobProcessor( apRequestService.apPost( param.inbox, Undo( - name = "Undo Reaction", actor = param.actor, `object` = like, id = "${applicationConfig.url}/undo/like/${param.id}", - published = Instant.now() + published = Instant.now().toString() ), signer ) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt index d69eb9d6..31a2b505 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt @@ -57,13 +57,11 @@ class APUserServiceImpl( url = userUrl, icon = Image( type = emptyList(), - name = "$userUrl/icon.png", mediaType = "image/png", url = "$userUrl/icon.png" ), publicKey = Key( type = emptyList(), - name = "Public Key", id = userEntity.keyId, owner = userUrl, publicKeyPem = userEntity.publicKey @@ -127,13 +125,11 @@ class APUserServiceImpl( url = id, icon = Image( type = emptyList(), - name = "$id/icon.png", mediaType = "image/png", url = "$id/icon.png" ), publicKey = Key( type = emptyList(), - name = "Public Key", id = userEntity.keyId, owner = id, publicKeyPem = userEntity.publicKey diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt index d6bdf301..d661a30a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt @@ -25,6 +25,7 @@ class ActivityPubConfig { .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) .setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY)) + .setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY)) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .configure(JsonParser.Feature.ALLOW_COMMENTS, true) .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DeleteSerializeTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DeleteSerializeTest.kt index 4f190250..21de8c61 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DeleteSerializeTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DeleteSerializeTest.kt @@ -46,7 +46,6 @@ class DeleteSerializeTest { val readValue = objectMapper.readValue(json) val expected = Delete( - name = null, actor = "https://misskey.usbharu.dev/users/97ws8y3rj6", id = "https://misskey.usbharu.dev/4b5b6ed5-9269-45f3-8403-cba1e74b4b69", `object` = Tombstone( @@ -61,7 +60,6 @@ class DeleteSerializeTest { @Test fun シリアライズできる() { val delete = Delete( - name = null, actor = "https://misskey.usbharu.dev/users/97ws8y3rj6", id = "https://misskey.usbharu.dev/4b5b6ed5-9269-45f3-8403-cba1e74b4b69", `object` = Tombstone( @@ -75,7 +73,7 @@ class DeleteSerializeTest { val actual = objectMapper.writeValueAsString(delete) val expected = - """{"type":"Delete","actor":"https://misskey.usbharu.dev/users/97ws8y3rj6","id":"https://misskey.usbharu.dev/4b5b6ed5-9269-45f3-8403-cba1e74b4b69","object":{"type":"Tombstone","name":"Tombstone","id":"https://misskey.usbharu.dev/notes/9lkwqnwqk9"},"published":"2023-11-02T15:30:34.160Z"}""" + """{"type":"Delete","actor":"https://misskey.usbharu.dev/users/97ws8y3rj6","id":"https://misskey.usbharu.dev/4b5b6ed5-9269-45f3-8403-cba1e74b4b69","object":{"type":"Tombstone","id":"https://misskey.usbharu.dev/notes/9lkwqnwqk9"},"published":"2023-11-02T15:30:34.160Z"}""" assertEquals(expected, actual) } } diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt index 1b05eef1..9e1397a9 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt @@ -10,7 +10,6 @@ class NoteSerializeTest { @Test fun Noteのシリアライズができる() { val note = Note( - name = "Note", id = "https://example.com", attributedTo = "https://example.com/actor", content = "Hello", @@ -22,7 +21,7 @@ class NoteSerializeTest { val writeValueAsString = objectMapper.writeValueAsString(note) assertEquals( - "{\"type\":\"Note\",\"name\":\"Note\",\"id\":\"https://example.com\",\"attributedTo\":\"https://example.com/actor\",\"content\":\"Hello\",\"published\":\"2023-05-20T10:28:17.308Z\",\"sensitive\":false}", + """{"type":"Note","id":"https://example.com","attributedTo":"https://example.com/actor","content":"Hello","published":"2023-05-20T10:28:17.308Z","sensitive":false}""", writeValueAsString ) } @@ -65,7 +64,6 @@ class NoteSerializeTest { val readValue = objectMapper.readValue(json) val note = Note( - name = "", id = "https://misskey.usbharu.dev/notes/9f2i9cm88e", type = listOf("Note"), attributedTo = "https://misskey.usbharu.dev/users/97ws8y3rj6", @@ -77,7 +75,6 @@ class NoteSerializeTest { inReplyTo = "https://calckey.jp/notes/9f2i7ymf1d", attachment = emptyList() ) - note.name = null assertEquals(note, readValue) } } diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/UndoTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/UndoTest.kt index 97ba9bc4..ea279694 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/UndoTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/UndoTest.kt @@ -1,8 +1,8 @@ package dev.usbharu.hideout.activitypub.domain.model +import dev.usbharu.hideout.application.config.ActivityPubConfig import org.intellij.lang.annotations.Language import org.junit.jupiter.api.Test -import utils.JsonObjectMapper import java.time.Clock import java.time.Instant import java.time.ZoneId @@ -12,18 +12,16 @@ class UndoTest { fun Undoのシリアライズができる() { val undo = Undo( emptyList(), - "Undo Follow", "https://follower.example.com/", "https://follower.example.com/undo/1", Follow( emptyList(), - null, "https://follower.example.com/users/", actor = "https://follower.exaple.com/users/1" ), - Instant.now(Clock.tickMillis(ZoneId.systemDefault())) + Instant.now(Clock.tickMillis(ZoneId.systemDefault())).toString() ) - val writeValueAsString = JsonObjectMapper.objectMapper.writeValueAsString(undo) + val writeValueAsString = ActivityPubConfig().objectMapper().writeValueAsString(undo) println(writeValueAsString) } @@ -70,7 +68,7 @@ class UndoTest { """.trimIndent() - val undo = JsonObjectMapper.objectMapper.readValue(json, Undo::class.java) + val undo = ActivityPubConfig().objectMapper().readValue(json, Undo::class.java) println(undo) } } diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectSerializeTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectSerializeTest.kt index 77f2579b..41bc114e 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectSerializeTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectSerializeTest.kt @@ -16,10 +16,7 @@ class ObjectSerializeTest { val readValue = objectMapper.readValue(json) val expected = Object( - listOf("Object"), - null, - null, - null + listOf("Object") ) assertEquals(expected, readValue) } @@ -34,10 +31,7 @@ class ObjectSerializeTest { val readValue = objectMapper.readValue(json) val expected = Object( - listOf("Hoge", "Object"), - null, - null, - null + listOf("Hoge", "Object") ) assertEquals(expected, readValue) @@ -53,10 +47,7 @@ class ObjectSerializeTest { val readValue = objectMapper.readValue(json) val expected = Object( - emptyList(), - null, - null, - null + emptyList() ) assertEquals(expected, readValue) diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImplTest.kt index 9520eac2..42f44e27 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImplTest.kt @@ -49,12 +49,10 @@ class UserAPControllerImplTest { outbox = "https://example.com/users/hoge/outbox", url = "https://example.com/users/hoge", icon = Image( - name = "icon", mediaType = "image/jpeg", url = "https://example.com/users/hoge/icon.jpg" ), publicKey = Key( - name = "Public Key", id = "https://example.com/users/hoge#pubkey", owner = "https://example.com/users/hoge", publicKeyPem = "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImplTest.kt index c337e770..bfa7168e 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImplTest.kt @@ -53,7 +53,6 @@ class NoteApControllerImplTest { fun `postAP 匿名で取得できる`() = runTest { SecurityContextHolder.clearContext() val note = Note( - name = "Note", id = "https://example.com/users/hoge/posts/1234", attributedTo = "https://example.com/users/hoge", content = "Hello", @@ -90,7 +89,6 @@ class NoteApControllerImplTest { @Test fun `postAP 認証に成功している場合userIdがnullでない`() = runTest { val note = Note( - name = "Note", id = "https://example.com/users/hoge/posts/1234", attributedTo = "https://example.com/users/hoge", content = "Hello", diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImplTest.kt index 47b5671e..01975882 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImplTest.kt @@ -52,7 +52,6 @@ class ApSendCreateServiceImplTest { val post = PostBuilder.of() val user = UserBuilder.localUserOf(id = post.userId) val note = Note( - name = "Post", id = post.apId, attributedTo = user.url, content = post.text, diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt index 6ce3a084..93e1d76d 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt @@ -24,7 +24,6 @@ class APSendFollowServiceImplTest { apSendFollowServiceImpl.sendFollow(sendFollowDto) val value = Follow( - name = "Follow", `object` = sendFollowDto.followTargetUserId.url, actor = sendFollowDto.userId.url ) diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt index ec36d233..2c609183 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt @@ -39,7 +39,7 @@ class APRequestServiceImplTest { assertDoesNotThrow { dateTimeFormatter.parse(it.headers["Date"]) } - respond("{}") + respond("""{"type":"Follow","object": "https://example.com","actor": "https://example.com"}""") }), objectMapper, mock(), @@ -47,7 +47,6 @@ class APRequestServiceImplTest { ) val responseClass = Follow( - name = "Follow", `object` = "https://example.com", actor = "https://example.com" ) @@ -65,7 +64,7 @@ class APRequestServiceImplTest { assertDoesNotThrow { dateTimeFormatter.parse(it.headers["Date"]) } - respond("{}") + respond("""{"type":"Follow","object": "https://example.com","actor": "https://example.com"}""") }), objectMapper, mock(), @@ -73,7 +72,6 @@ class APRequestServiceImplTest { ) val responseClass = Follow( - name = "Follow", `object` = "https://example.com", actor = "https://example.com" ) @@ -106,7 +104,7 @@ class APRequestServiceImplTest { assertDoesNotThrow { dateTimeFormatter.parse(it.headers["Date"]) } - respond("{}") + respond("""{"type":"Follow","object": "https://example.com","actor": "https://example.com"}""") }), objectMapper, httpSignatureSigner, @@ -114,7 +112,6 @@ class APRequestServiceImplTest { ) val responseClass = Follow( - name = "Follow", `object` = "https://example.com", actor = "https://example.com" ) @@ -166,7 +163,6 @@ class APRequestServiceImplTest { }), objectMapper, mock(), dateTimeFormatter) val body = Follow( - name = "Follow", `object` = "https://example.com", actor = "https://example.com" ) @@ -213,7 +209,6 @@ class APRequestServiceImplTest { }), objectMapper, mock(), dateTimeFormatter) val body = Follow( - name = "Follow", `object` = "https://example.com", actor = "https://example.com" ) @@ -244,7 +239,6 @@ class APRequestServiceImplTest { }), objectMapper, mock(), dateTimeFormatter) val body = Follow( - name = "Follow", `object` = "https://example.com", actor = "https://example.com" ) @@ -286,7 +280,6 @@ class APRequestServiceImplTest { }), objectMapper, httpSignatureSigner, dateTimeFormatter) val body = Follow( - name = "Follow", `object` = "https://example.com", actor = "https://example.com" ) @@ -337,7 +330,6 @@ class APRequestServiceImplTest { }), objectMapper, mock(), dateTimeFormatter) val body = Follow( - name = "Follow", `object` = "https://example.com", actor = "https://example.com" ) diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt index 540f642c..5ed6597d 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt @@ -56,7 +56,6 @@ class APNoteServiceImplTest { onBlocking { findById(eq(post.userId)) } doReturn user } val expected = Note( - name = "Post", id = post.apId, attributedTo = user.url, content = post.text, @@ -98,7 +97,6 @@ class APNoteServiceImplTest { onBlocking { findById(eq(post.userId)) } doReturn user } val note = Note( - name = "Post", id = post.apId, attributedTo = user.url, content = post.text, @@ -124,13 +122,11 @@ class APNoteServiceImplTest { url = user.url, icon = Image( type = emptyList(), - name = user.url + "/icon.png", mediaType = "image/png", url = user.url + "/icon.png" ), publicKey = Key( type = emptyList(), - name = "Public Key", id = user.keyId, owner = user.url, publicKeyPem = user.publicKey @@ -177,7 +173,6 @@ class APNoteServiceImplTest { onBlocking { findById(eq(post.userId)) } doReturn user } val note = Note( - name = "Post", id = post.apId, attributedTo = user.url, content = post.text, @@ -246,11 +241,11 @@ class APNoteServiceImplTest { outbox = user.outbox, url = user.url, icon = Image( - name = user.url + "/icon.png", mediaType = "image/png", url = user.url + "/icon.png" + mediaType = "image/png", + url = user.url + "/icon.png" ), publicKey = Key( type = emptyList(), - name = "Public Key", id = user.keyId, owner = user.url, publicKeyPem = user.publicKey @@ -278,7 +273,6 @@ class APNoteServiceImplTest { ) val note = Note( - name = "Post", id = post.apId, attributedTo = user.url, content = post.text, @@ -311,7 +305,6 @@ class APNoteServiceImplTest { onBlocking { findById(eq(user.id)) } doReturn user } val note = Note( - name = "Post", id = post.apId, attributedTo = user.url, content = post.text, diff --git a/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt b/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt index db434005..d73e31d7 100644 --- a/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt @@ -13,7 +13,6 @@ class ContextSerializerTest { name = "aaa", actor = "bbb", `object` = Follow( - name = "ccc", `object` = "ddd", actor = "aaa" ) From 4542fdf68b4e18c57d71e8169ce0ba0a9cddea9b Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:10:40 +0900 Subject: [PATCH 038/116] =?UTF-8?q?reafactor:=20=E3=81=9D=E3=81=AE?= =?UTF-8?q?=E4=BB=96=E3=81=AE=E9=83=A8=E5=88=86=E3=82=82Null-safe=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/domain/model/Accept.kt | 4 +- .../activitypub/domain/model/Create.kt | 6 +-- .../activitypub/domain/model/Delete.kt | 6 +-- .../activitypub/domain/model/Document.kt | 7 +--- .../hideout/activitypub/domain/model/Emoji.kt | 4 +- .../activitypub/domain/model/Follow.kt | 22 ++++------- .../hideout/activitypub/domain/model/Image.kt | 18 ++++----- .../hideout/activitypub/domain/model/Key.kt | 26 +++++-------- .../hideout/activitypub/domain/model/Like.kt | 38 ++++++------------- .../hideout/activitypub/domain/model/Note.kt | 16 ++++---- .../activitypub/domain/model/Person.kt | 6 +-- .../hideout/activitypub/domain/model/Undo.kt | 27 ++++--------- 12 files changed, 64 insertions(+), 116 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt index d1b0936b..15eaf20f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt @@ -8,9 +8,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer open class Accept @JsonCreator constructor( type: List = emptyList(), override val name: String, - @JsonDeserialize(using = ObjectDeserializer::class) - @Suppress("VariableNaming") - var `object`: Object?, + @JsonDeserialize(using = ObjectDeserializer::class) @Suppress("VariableNaming") var `object`: Object?, override val actor: String ) : Object( type = add(type, "Accept") diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt index df22045c..6b6bb810 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt @@ -9,11 +9,11 @@ open class Create( override val name: String, @JsonDeserialize(using = ObjectDeserializer::class) @Suppress("VariableNaming") - var `object`: Object?, + val `object`: Object, override val actor: String, override val id: String, - var to: List = emptyList(), - var cc: List = emptyList() + val to: List = emptyList(), + val cc: List = emptyList() ) : Object( type = add(type, "Create") ), diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt index 10b919c5..e4e8ccb0 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt @@ -7,8 +7,8 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer open class Delete : Object, HasId, HasActor { @JsonDeserialize(using = ObjectDeserializer::class) @Suppress("VariableNaming") - var `object`: Object? = null - var published: String? = null + val `object`: Object + val published: String override val actor: String override val id: String @@ -17,7 +17,7 @@ open class Delete : Object, HasId, HasActor { actor: String, id: String, `object`: Object, - published: String? + published: String ) : super(add(type, "Delete")) { this.`object` = `object` this.published = published diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt index 480c6011..a9b3e8c9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt @@ -5,16 +5,13 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.Object open class Document( type: List = emptyList(), override val name: String = "", - mediaType: String, - url: String + val mediaType: String, + val url: String ) : Object( type = add(type, "Document") ), HasName { - var mediaType: String? = mediaType - var url: String? = url - override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt index 0a91be60..d46edc8a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt @@ -6,8 +6,8 @@ open class Emoji( type: List, override val name: String, override val id: String, - var updated: String?, - var icon: Image? + val updated: String, + val icon: Image ) : Object( type = add(type, "Emoji") ), diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt index 73ff75f3..2689ccb5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt @@ -2,22 +2,14 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object -open class Follow : Object, HasActor { - @Suppress("VariableNaming") - var `object`: String? = null - +open class Follow( + type: List = emptyList(), + @Suppress("VariableNaming") val `object`: String, override val actor: String - - constructor( - type: List = emptyList(), - `object`: String?, - actor: String - ) : super( - type = add(type, "Follow") - ) { - this.`object` = `object` - this.actor = actor - } +) : Object( + type = add(type, "Follow") +), + HasActor { override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt index 60b0c8ac..6353692b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt @@ -2,17 +2,13 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object -open class Image : Object { - private var mediaType: String? = null - private var url: String? = null - - protected constructor() : super() - constructor(type: List = emptyList(), mediaType: String?, url: String?) : super( - add(type, "Image") - ) { - this.mediaType = mediaType - this.url = url - } +open class Image( + type: List = emptyList(), + val mediaType: String, + val url: String +) : Object( + add(type, "Image") +) { override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt index 993d4af9..4a13ba56 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt @@ -2,23 +2,15 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object -open class Key : Object, HasId { - var owner: String? = null - var publicKeyPem: String? = null - override val id: String - - constructor( - type: List, - id: String, - owner: String?, - publicKeyPem: String? - ) : super( - type = add(list = type, type = "Key") - ) { - this.owner = owner - this.publicKeyPem = publicKeyPem - this.id = id - } +open class Key( + type: List, + override val id: String, + val owner: String, + val publicKeyPem: String +) : Object( + type = add(list = type, type = "Key") +), + HasId { override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt index 2400eeff..f4eaa13f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt @@ -4,32 +4,18 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer -open class Like : Object, HasId, HasActor { - @Suppress("VariableNaming") - var `object`: String? = null - var content: String? = null - - @JsonDeserialize(contentUsing = ObjectDeserializer::class) - var tag: List = emptyList() - override val actor: String - override val id: String - - constructor( - type: List = emptyList(), - actor: String, - id: String, - `object`: String?, - content: String?, - tag: List = emptyList() - ) : super( - type = add(type, "Like") - ) { - this.`object` = `object` - this.content = content - this.tag = tag - this.actor = actor - this.id = id - } +open class Like( + type: List = emptyList(), + override val actor: String, + override val id: String, + @Suppress("VariableNaming") val `object`: String, + val content: String, + @JsonDeserialize(contentUsing = ObjectDeserializer::class) val tag: List = emptyList() +) : Object( + type = add(type, "Like") +), + HasId, + HasActor { override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt index 74d5ce7a..889c8776 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt @@ -7,14 +7,14 @@ open class Note constructor( type: List = emptyList(), override val id: String, - var attributedTo: String, - var content: String, - var published: String, - var to: List = emptyList(), - var cc: List = emptyList(), - var sensitive: Boolean = false, - var inReplyTo: String? = null, - var attachment: List = emptyList() + val attributedTo: String, + val content: String, + val published: String, + val to: List = emptyList(), + val cc: List = emptyList(), + val sensitive: Boolean = false, + val inReplyTo: String? = null, + val attachment: List = emptyList() ) : Object( type = add(type, "Note") ), diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt index e94cc9b1..58165a81 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt @@ -10,9 +10,9 @@ constructor( override val id: String, var preferredUsername: String?, var summary: String?, - var inbox: String?, - var outbox: String?, - var url: String?, + var inbox: String, + var outbox: String, + var url: String, private var icon: Image?, var publicKey: Key?, var endpoints: Map = emptyMap(), diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt index 857416ef..d02ccc4d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt @@ -4,27 +4,14 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer -open class Undo : Object, HasId, HasActor { - +open class Undo( + type: List = emptyList(), + override val actor: String, + override val id: String, @JsonDeserialize(using = ObjectDeserializer::class) - @Suppress("VariableNaming") - var `object`: Object? = null - var published: String? = null - override val actor: String - override val id: String - - constructor( - type: List = emptyList(), - actor: String, - id: String, - `object`: Object, - published: String - ) : super(add(type, "Undo")) { - this.`object` = `object` - this.published = published - this.id = id - this.actor = actor - } + @Suppress("VariableNaming") val `object`: Object, + val published: String +) : Object(add(type, "Undo")), HasId, HasActor { override fun equals(other: Any?): Boolean { if (this === other) return true From 34d8eabea1e39607e7ee9c2632a3ea7febc9d811 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:28:32 +0900 Subject: [PATCH 039/116] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E5=AE=A3=E8=A8=80=E7=AD=89=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/FailedProcessException.kt | 7 +++++ ...FailedToGetActivityPubResourceException.kt | 6 +++++ .../HttpSignatureUnauthorizedException.kt | 7 +++++ .../activitypub/domain/model/Document.kt | 4 +-- .../hideout/activitypub/domain/model/Emoji.kt | 4 +-- .../activitypub/domain/model/Follow.kt | 2 +- .../hideout/activitypub/domain/model/Image.kt | 4 +-- .../hideout/activitypub/domain/model/Key.kt | 4 +-- .../hideout/activitypub/domain/model/Note.kt | 16 +++++++++-- .../activitypub/domain/model/Person.kt | 6 ++--- .../activity/accept/ApAcceptProcessor.kt | 8 +++--- .../create/CreateActivityProcessor.kt | 2 +- .../activity/follow/APFollowProcessor.kt | 5 ++-- .../follow/APReceiveFollowJobProcessor.kt | 4 +-- .../service/activity/like/APLikeProcessor.kt | 7 +++-- .../service/activity/undo/APUndoProcessor.kt | 8 +++--- .../service/common/APRequestServiceImpl.kt | 14 +++++----- .../common/AbstractActivityPubProcessor.kt | 3 ++- .../service/objects/note/APNoteService.kt | 14 +++++----- .../service/objects/user/APUserService.kt | 8 +++--- .../exception/media/MediaConvertException.kt | 7 +++++ .../domain/exception/media/MediaException.kt | 7 +++++ .../exception/media/MediaFileSizeException.kt | 7 +++++ .../media/MediaFileSizeIsZeroException.kt | 7 +++++ .../exception/media/MediaProcessException.kt | 7 +++++ .../media/UnsupportedMediaException.kt | 7 +++++ .../core/domain/model/instance/Nodeinfo.kt | 8 ++---- .../core/domain/model/instance/Nodeinfo2_0.kt | 12 +++------ .../InstanceRepositoryImpl.kt | 2 +- .../ExposedOAuth2AuthorizationService.kt | 2 +- .../core/service/instance/InstanceService.kt | 4 +-- .../hideout/core/service/media/MediaSave.kt | 27 ++++++++++++++++++- .../core/service/media/MediaServiceImpl.kt | 2 +- .../core/service/media/ProcessedFile.kt | 20 +++++++++++++- .../service/reaction/ReactionServiceImpl.kt | 3 ++- .../service/resource/InMemoryCacheManager.kt | 2 +- .../core/service/user/UserServiceImpl.kt | 3 +-- .../JsonOrFormModelMethodProcessor.kt | 3 ++- .../exposedquery/StatusQueryServiceImpl.kt | 1 + .../service/media/MediaApiServiceImpl.kt | 1 - .../dev/usbharu/hideout/util/AcctUtil.kt | 2 +- .../dev/usbharu/hideout/util/HttpUtil.kt | 4 +-- 42 files changed, 189 insertions(+), 82 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt index 31c4b47e..144759d3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.activitypub.domain.exception +import java.io.Serial + class FailedProcessException : RuntimeException { constructor() : super() constructor(message: String?) : super(message) @@ -11,4 +13,9 @@ class FailedProcessException : RuntimeException { enableSuppression, writableStackTrace ) + + companion object { + @Serial + private const val serialVersionUID: Long = -1305337651143409144L + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedToGetActivityPubResourceException.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedToGetActivityPubResourceException.kt index e050e716..ed967555 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedToGetActivityPubResourceException.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedToGetActivityPubResourceException.kt @@ -1,10 +1,16 @@ package dev.usbharu.hideout.activitypub.domain.exception import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import java.io.Serial class FailedToGetActivityPubResourceException : FailedToGetResourcesException { constructor() : super() constructor(s: String?) : super(s) constructor(message: String?, cause: Throwable?) : super(message, cause) constructor(cause: Throwable?) : super(cause) + + companion object { + @Serial + private const val serialVersionUID: Long = 6420233106776818052L + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt index 9abccec6..3bba00ef 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.activitypub.domain.exception +import java.io.Serial + class HttpSignatureUnauthorizedException : RuntimeException { constructor() : super() constructor(message: String?) : super(message) @@ -11,4 +13,9 @@ class HttpSignatureUnauthorizedException : RuntimeException { enableSuppression, writableStackTrace ) + + companion object { + @Serial + private const val serialVersionUID: Long = -6449793151674654501L + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt index a9b3e8c9..d8b7ff7e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt @@ -28,8 +28,8 @@ open class Document( override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (mediaType?.hashCode() ?: 0) - result = 31 * result + (url?.hashCode() ?: 0) + result = 31 * result + mediaType.hashCode() + result = 31 * result + url.hashCode() result = 31 * result + name.hashCode() return result } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt index d46edc8a..37ebb879 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt @@ -25,8 +25,8 @@ open class Emoji( override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (updated?.hashCode() ?: 0) - result = 31 * result + (icon?.hashCode() ?: 0) + result = 31 * result + updated.hashCode() + result = 31 * result + icon.hashCode() return result } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt index 2689ccb5..d4ef2c95 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt @@ -26,7 +26,7 @@ open class Follow( override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (`object`?.hashCode() ?: 0) + result = 31 * result + `object`.hashCode() result = 31 * result + actor.hashCode() return result } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt index 6353692b..5b63ef5e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt @@ -25,8 +25,8 @@ open class Image( override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (mediaType?.hashCode() ?: 0) - result = 31 * result + (url?.hashCode() ?: 0) + result = 31 * result + mediaType.hashCode() + result = 31 * result + url.hashCode() return result } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt index 4a13ba56..7e22097c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt @@ -28,8 +28,8 @@ open class Key( override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (owner?.hashCode() ?: 0) - result = 31 * result + (publicKeyPem?.hashCode() ?: 0) + result = 31 * result + owner.hashCode() + result = 31 * result + publicKeyPem.hashCode() result = 31 * result + id.hashCode() return result } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt index 889c8776..91bf8a86 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt @@ -54,6 +54,18 @@ constructor( return result } - override fun toString(): String = - "Note(id='$id', attributedTo='$attributedTo', content='$content', published='$published', to=$to, cc=$cc, sensitive=$sensitive, inReplyTo=$inReplyTo, attachment=$attachment) ${super.toString()}" + override fun toString(): String { + return "Note(" + + "id='$id', " + + "attributedTo='$attributedTo', " + + "content='$content', " + + "published='$published', " + + "to=$to, " + + "cc=$cc, " + + "sensitive=$sensitive, " + + "inReplyTo=$inReplyTo, " + + "attachment=$attachment" + + ")" + + " ${super.toString()}" + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt index 58165a81..fe1b2d5f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt @@ -41,9 +41,9 @@ constructor( var result = super.hashCode() result = 31 * result + (preferredUsername?.hashCode() ?: 0) result = 31 * result + (summary?.hashCode() ?: 0) - result = 31 * result + (inbox?.hashCode() ?: 0) - result = 31 * result + (outbox?.hashCode() ?: 0) - result = 31 * result + (url?.hashCode() ?: 0) + result = 31 * result + inbox.hashCode() + result = 31 * result + outbox.hashCode() + result = 31 * result + url.hashCode() result = 31 * result + (icon?.hashCode() ?: 0) result = 31 * result + (publicKey?.hashCode() ?: 0) result = 31 * result + endpoints.hashCode() diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt index 6060531f..0567bf5f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt @@ -12,7 +12,7 @@ import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.user.UserService class ApAcceptProcessor( - private val transaction: Transaction, + transaction: Transaction, private val userQueryService: UserQueryService, private val followerQueryService: FollowerQueryService, private val userService: UserService @@ -23,14 +23,14 @@ class ApAcceptProcessor( val value = activity.activity.`object` ?: throw IllegalActivityPubObjectException("object is null") if (value.type.contains("Follow").not()) { - logger.warn("FAILED Activity type is not Follow.") + logger.warn("FAILED Activity type isn't Follow.") throw IllegalActivityPubObjectException("Invalid type ${value.type}") } val follow = value as Follow - val userUrl = follow.`object` ?: throw IllegalActivityPubObjectException("object is null") - val followerUrl = follow.actor ?: throw IllegalActivityPubObjectException("actor is null") + val userUrl = follow.`object` + val followerUrl = follow.actor val user = userQueryService.findByUrl(userUrl) val follower = userQueryService.findByUrl(followerUrl) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt index 5d057b3f..2e1b557c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt @@ -11,7 +11,7 @@ import org.springframework.stereotype.Service @Service class CreateActivityProcessor(transaction: Transaction, private val apNoteService: APNoteService) : - AbstractActivityPubProcessor(transaction, false) { + AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { apNoteService.fetchNote(activity.activity.`object` as Note) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt index 92ad3e28..b56cd49f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt @@ -1,7 +1,6 @@ package dev.usbharu.hideout.activitypub.service.activity.follow import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext @@ -22,9 +21,9 @@ class APFollowProcessor( // inboxをジョブキューに乗せているので既に不要だが、フォロー承認制アカウントを実装する際に必要なので残す val jobProps = ReceiveFollowJobParam( - activity.activity.actor ?: throw IllegalActivityPubObjectException("actor is null"), + activity.activity.actor, objectMapper.writeValueAsString(activity.activity), - activity.activity.`object` ?: throw IllegalActivityPubObjectException("object is null") + activity.activity.`object` ) jobQueueParentService.scheduleTypeSafe(ReceiveFollowJob, jobProps) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt index a6aae23b..f8cf8556 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt @@ -33,7 +33,7 @@ class APReceiveFollowJobProcessor( val signer = userQueryService.findByUrl(param.targetActor) - val urlString = person.inbox ?: throw IllegalArgumentException("inbox is not found.") + val urlString = person.inbox apRequestService.apPost( url = urlString, @@ -47,7 +47,7 @@ class APReceiveFollowJobProcessor( val targetEntity = userQueryService.findByUrl(param.targetActor) val followActorEntity = - userQueryService.findByUrl(follow.actor ?: throw IllegalArgumentException("actor is null")) + userQueryService.findByUrl(follow.actor) userService.followRequest(targetEntity.id, followActorEntity.id) logger.info("SUCCESS Follow from: {} to: {}", param.targetActor, param.actor) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt index 470eaee7..9d56fe94 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt @@ -1,7 +1,6 @@ package dev.usbharu.hideout.activitypub.service.activity.like import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException import dev.usbharu.hideout.activitypub.domain.model.Like import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext @@ -21,10 +20,10 @@ class APLikeProcessor( ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val actor = activity.activity.actor ?: throw IllegalActivityPubObjectException("actor is null") - val content = activity.activity.content ?: throw IllegalActivityPubObjectException("content is null") + val actor = activity.activity.actor + val content = activity.activity.content - val target = activity.activity.`object` ?: throw IllegalActivityPubObjectException("object is null") + val target = activity.activity.`object` val personWithEntity = apUserService.fetchPersonWithEntity(actor) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt index 8c09b54f..99bd9ba1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt @@ -24,7 +24,7 @@ class APUndoProcessor( } val type = - undo.`object`?.type.orEmpty() + undo.`object`.type.orEmpty() .firstOrNull { it == "Block" || it == "Follow" || it == "Like" || it == "Announce" || it == "Accept" } ?: return @@ -35,9 +35,9 @@ class APUndoProcessor( if (follow.`object` == null) { return } - apUserService.fetchPerson(undo.actor!!, follow.`object`) - val follower = userQueryService.findByUrl(undo.actor!!) - val target = userQueryService.findByUrl(follow.`object`!!) + apUserService.fetchPerson(undo.actor, follow.`object`) + val follower = userQueryService.findByUrl(undo.actor) + val target = userQueryService.findByUrl(follow.`object`) userService.unfollow(target.id, follower.id) return } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt index bf13b1ca..511d3e67 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt @@ -61,7 +61,7 @@ class APRequestServiceImpl( url: String ): HttpResponse { val headers = headers { - append("Accept", ContentType.Application.Activity) + append("Accept", Activity) append("Date", date) append("Host", u.host) } @@ -87,13 +87,13 @@ class APRequestServiceImpl( remove("Host") } } - contentType(ContentType.Application.Activity) + contentType(Activity) } return httpResponse } private suspend fun apGetNotSign(url: String, date: String?) = httpClient.get(url) { - header("Accept", ContentType.Application.Activity) + header("Accept", Activity) header("Date", date) } @@ -153,12 +153,12 @@ class APRequestServiceImpl( digest: String, requestBody: String? ) = httpClient.post(url) { - accept(ContentType.Application.Activity) + accept(Activity) header("Date", date) header("Digest", "sha-256=$digest") if (requestBody != null) { setBody(requestBody) - contentType(ContentType.Application.Activity) + contentType(Activity) } } @@ -170,7 +170,7 @@ class APRequestServiceImpl( requestBody: String? ): HttpResponse { val headers = headers { - append("Accept", ContentType.Application.Activity) + append("Accept", Activity) append("Date", date) append("Host", u.host) append("Digest", "sha-256=$digest") @@ -196,7 +196,7 @@ class APRequestServiceImpl( remove("Host") } setBody(requestBody) - contentType(ContentType.Application.Activity) + contentType(Activity) } return httpResponse } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt index 329941b2..1bb106e3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt @@ -5,13 +5,14 @@ import dev.usbharu.hideout.activitypub.domain.exception.FailedProcessException import dev.usbharu.hideout.activitypub.domain.exception.HttpSignatureUnauthorizedException import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.application.external.Transaction +import org.slf4j.Logger import org.slf4j.LoggerFactory abstract class AbstractActivityPubProcessor( private val transaction: Transaction, private val allowUnauthorized: Boolean = false ) : ActivityPubProcessor { - protected val logger = LoggerFactory.getLogger(this::class.java) + protected val logger: Logger = LoggerFactory.getLogger(this::class.java) override suspend fun process(activity: ActivityPubProcessContext) { if (activity.isAuthorized.not() && allowUnauthorized.not()) { diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt index 28bf7c01..0e5b6e14 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt @@ -90,7 +90,7 @@ class APNoteServiceImpl( requireNotNull(note.id) { "id is null" } return try { - noteQueryService.findByApid(note.id!!).first + noteQueryService.findByApid(note.id).first } catch (_: FailedToGetResourcesException) { saveNote(note, targetActor, url) } @@ -127,9 +127,9 @@ class APNoteServiceImpl( .map { mediaService.uploadRemoteMedia( RemoteMedia( - (it.name ?: it.url)!!, - it.url!!, - it.mediaType ?: "application/octet-stream", + it.name, + it.url, + it.mediaType, description = it.name ) ) @@ -144,10 +144,10 @@ class APNoteServiceImpl( text = note.content, createdAt = Instant.parse(note.published).toEpochMilli(), visibility = visibility, - url = note.id ?: url, + url = note.id, replyId = reply?.id, sensitive = note.sensitive, - apId = note.id ?: url, + apId = note.id, mediaIds = mediaList ) ) @@ -155,7 +155,7 @@ class APNoteServiceImpl( } override suspend fun fetchNote(note: Note, targetActor: String?): Note = - saveIfMissing(note, targetActor, note.id ?: throw IllegalArgumentException("note.id is null")) + saveIfMissing(note, targetActor, note.id) companion object { const val public: String = "https://www.w3.org/ns/activitystreams#Public" diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt index 31a2b505..143df20b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt @@ -83,7 +83,7 @@ class APUserServiceImpl( } catch (ignore: FailedToGetResourcesException) { val person = apResourceResolveService.resolve(url, null as Long?) - val id = person.id ?: throw IllegalActivityPubObjectException("id is null") + val id = person.id try { val userEntity = userQueryService.findByUrl(id) return entityToPerson(userEntity, id) to userEntity @@ -94,11 +94,11 @@ class APUserServiceImpl( name = person.preferredUsername ?: throw IllegalActivityPubObjectException("preferredUsername is null"), domain = id.substringAfter("://").substringBefore("/"), - screenName = (person.name ?: person.preferredUsername) + screenName = person.name ?: throw IllegalActivityPubObjectException("preferredUsername is null"), description = person.summary.orEmpty(), - inbox = person.inbox ?: throw IllegalActivityPubObjectException("inbox is null"), - outbox = person.outbox ?: throw IllegalActivityPubObjectException("outbox is null"), + inbox = person.inbox, + outbox = person.outbox, url = id, publicKey = person.publicKey?.publicKeyPem ?: throw IllegalActivityPubObjectException("publicKey is null"), diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaConvertException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaConvertException.kt index d2d9d7c9..cc564619 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaConvertException.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaConvertException.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.core.domain.exception.media +import java.io.Serial + open class MediaConvertException : MediaException { constructor() : super() constructor(message: String?) : super(message) @@ -11,4 +13,9 @@ open class MediaConvertException : MediaException { enableSuppression, writableStackTrace ) + + companion object { + @Serial + private const val serialVersionUID: Long = -6349105549968160551L + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaException.kt index 538e3c72..221c92e5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaException.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaException.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.core.domain.exception.media +import java.io.Serial + abstract class MediaException : RuntimeException { constructor() : super() constructor(message: String?) : super(message) @@ -11,4 +13,9 @@ abstract class MediaException : RuntimeException { enableSuppression, writableStackTrace ) + + companion object { + @Serial + private const val serialVersionUID: Long = 5988922562494187852L + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeException.kt index f75b74c2..f5153641 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeException.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeException.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.core.domain.exception.media +import java.io.Serial + open class MediaFileSizeException : MediaException { constructor() : super() constructor(message: String?) : super(message) @@ -11,4 +13,9 @@ open class MediaFileSizeException : MediaException { enableSuppression, writableStackTrace ) + + companion object { + @Serial + private const val serialVersionUID: Long = 8672626879026555064L + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeIsZeroException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeIsZeroException.kt index 74261698..1837ce06 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeIsZeroException.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeIsZeroException.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.core.domain.exception.media +import java.io.Serial + class MediaFileSizeIsZeroException : MediaFileSizeException { constructor() : super() constructor(message: String?) : super(message) @@ -11,4 +13,9 @@ class MediaFileSizeIsZeroException : MediaFileSizeException { enableSuppression, writableStackTrace ) + + companion object { + @Serial + private const val serialVersionUID: Long = -2398394583775317875L + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaProcessException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaProcessException.kt index 4292d0e8..6751fec1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaProcessException.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaProcessException.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.core.domain.exception.media +import java.io.Serial + class MediaProcessException : MediaException { constructor() : super() constructor(message: String?) : super(message) @@ -11,4 +13,9 @@ class MediaProcessException : MediaException { enableSuppression, writableStackTrace ) + + companion object { + @Serial + private const val serialVersionUID: Long = -5195233013542703735L + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/UnsupportedMediaException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/UnsupportedMediaException.kt index 7eea4e38..6c62f904 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/UnsupportedMediaException.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/UnsupportedMediaException.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.core.domain.exception.media +import java.io.Serial + class UnsupportedMediaException : MediaException { constructor() : super() constructor(message: String?) : super(message) @@ -11,4 +13,9 @@ class UnsupportedMediaException : MediaException { enableSuppression, writableStackTrace ) + + companion object { + @Serial + private const val serialVersionUID: Long = -116741513216017134L + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt index 427c76a3..f7fc3160 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt @@ -1,15 +1,11 @@ package dev.usbharu.hideout.core.domain.model.instance -class Nodeinfo { +class Nodeinfo private constructor() { var links: List = emptyList() - - private constructor() } -class Links { +class Links private constructor() { var rel: String? = null var href: String? = null - - private constructor() } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt index 53479eee..98247150 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt @@ -3,23 +3,17 @@ package dev.usbharu.hideout.core.domain.model.instance @Suppress("ClassNaming") -class Nodeinfo2_0 { +class Nodeinfo2_0() { var metadata: Metadata? = null var software: Software? = null - - constructor() } -class Metadata { +class Metadata() { var nodeName: String? = null var nodeDescription: String? = null - - constructor() } -class Software { +class Software() { var name: String? = null var version: String? = null - - constructor() } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt index a7d8aa9b..883a5b48 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt @@ -54,7 +54,7 @@ class InstanceRepositoryImpl(private val idGenerateService: IdGenerateService) : } override suspend fun delete(instance: InstanceEntity) { - Instance.deleteWhere { Instance.id eq instance.id } + Instance.deleteWhere { id eq instance.id } } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt index 580c4029..34e72833 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt @@ -62,7 +62,7 @@ class ExposedOAuth2AuthorizationService( it[accessTokenMetadata] = accessToken?.metadata?.let { it1 -> mapToJson(it1) } it[accessTokenType] = accessToken?.token?.tokenType?.value it[accessTokenScopes] = - accessToken?.run { token.scopes.joinToString(",").takeIf { it.isNotEmpty() } } + accessToken?.run { token.scopes.joinToString(",").takeIf { s -> s.isNotEmpty() } } it[refreshTokenValue] = refreshToken?.token?.tokenValue it[refreshTokenIssuedAt] = refreshToken?.token?.issuedAt it[refreshTokenExpiresAt] = refreshToken?.token?.expiresAt diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt index 41459964..2c773ce0 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt @@ -53,7 +53,7 @@ class InstanceServiceImpl( name = nodeinfo20.metadata?.nodeName, description = nodeinfo20.metadata?.nodeDescription, url = resolveInstanceUrl, - iconUrl = resolveInstanceUrl + "/favicon.ico", + iconUrl = "$resolveInstanceUrl/favicon.ico", sharedInbox = sharedInbox, software = nodeinfo20.software?.name, version = nodeinfo20.software?.version @@ -72,7 +72,7 @@ class InstanceServiceImpl( name = nodeinfo20.metadata?.nodeName, description = nodeinfo20.metadata?.nodeDescription, url = resolveInstanceUrl, - iconUrl = resolveInstanceUrl + "/favicon.ico", + iconUrl = "$resolveInstanceUrl/favicon.ico", sharedInbox = sharedInbox, software = nodeinfo20.software?.name, version = nodeinfo20.software?.version diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaSave.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaSave.kt index 3529ace9..9fbae73a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaSave.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaSave.kt @@ -5,4 +5,29 @@ data class MediaSave( val prefix: String, val fileInputStream: ByteArray, val thumbnailInputStream: ByteArray? -) +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as MediaSave + + if (name != other.name) return false + if (prefix != other.prefix) return false + if (!fileInputStream.contentEquals(other.fileInputStream)) return false + if (thumbnailInputStream != null) { + if (other.thumbnailInputStream == null) return false + if (!thumbnailInputStream.contentEquals(other.thumbnailInputStream)) return false + } else if (other.thumbnailInputStream != null) return false + + return true + } + + override fun hashCode(): Int { + var result = name.hashCode() + result = 31 * result + prefix.hashCode() + result = 31 * result + fileInputStream.contentHashCode() + result = 31 * result + (thumbnailInputStream?.contentHashCode() ?: 0) + return result + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt index ff8f1d93..5337e53a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt @@ -15,7 +15,7 @@ import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia @Service @Suppress("TooGenericExceptionCaught") -open class MediaServiceImpl( +class MediaServiceImpl( private val mediaDataStore: MediaDataStore, private val fileTypeDeterminationService: FileTypeDeterminationService, private val mediaBlurhashService: MediaBlurhashService, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedFile.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedFile.kt index c138ed43..b3589cee 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedFile.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedFile.kt @@ -3,4 +3,22 @@ package dev.usbharu.hideout.core.service.media data class ProcessedFile( val byteArray: ByteArray, val extension: String -) +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ProcessedFile + + if (!byteArray.contentEquals(other.byteArray)) return false + if (extension != other.extension) return false + + return true + } + + override fun hashCode(): Int { + var result = byteArray.contentHashCode() + result = 31 * result + extension.hashCode() + return result + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt index 056e5249..1ab87448 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt @@ -6,6 +6,7 @@ import dev.usbharu.hideout.core.domain.model.reaction.Reaction import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository import dev.usbharu.hideout.core.query.ReactionQueryService import org.jetbrains.exposed.exceptions.ExposedSQLException +import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -50,6 +51,6 @@ class ReactionServiceImpl( } companion object { - val LOGGER = LoggerFactory.getLogger(ReactionServiceImpl::class.java) + val LOGGER: Logger = LoggerFactory.getLogger(ReactionServiceImpl::class.java) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt index b48fadd7..587829c9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt @@ -18,7 +18,7 @@ class InMemoryCacheManager : CacheManager { keyMutex.withLock { cacheKey.filter { Instant.ofEpochMilli(it.value).plusSeconds(300) <= Instant.now() } - val cached = cacheKey.get(key) + val cached = cacheKey[key] if (cached == null) { needRunBlock = true cacheKey[key] = Instant.now().toEpochMilli() diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index 4bf358b7..fa1c0f97 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -52,8 +52,7 @@ class UserServiceImpl( createdAt = Instant.now(), following = "$userUrl/following", followers = "$userUrl/followers", - keyId = "$userUrl#pubkey", - instance = null + keyId = "$userUrl#pubkey" ) return userRepository.save(userEntity) } diff --git a/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt index a4222880..f784a4d2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.generate +import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.core.MethodParameter import org.springframework.web.bind.support.WebDataBinderFactory @@ -49,6 +50,6 @@ class JsonOrFormModelMethodProcessor( } companion object { - val logger = LoggerFactory.getLogger(JsonOrFormModelMethodProcessor::class.java) + val logger: Logger = LoggerFactory.getLogger(JsonOrFormModelMethodProcessor::class.java) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt index 5d113667..8010405d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt @@ -11,6 +11,7 @@ import org.jetbrains.exposed.sql.select import org.springframework.stereotype.Repository import java.time.Instant +@Suppress("IncompleteDestructuring") @Repository class StatusQueryServiceImpl : StatusQueryService { override suspend fun findByPostIds(ids: List): List = findByPostIdsWithMedia(ids) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiServiceImpl.kt index 824a6f59..c87b6469 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiServiceImpl.kt @@ -25,7 +25,6 @@ class MediaApiServiceImpl(private val mediaService: MediaService, private val tr type = type, url = uploadLocalMedia.url, previewUrl = uploadLocalMedia.thumbnailUrl, - remoteUrl = null, description = mediaRequest.description, blurhash = uploadLocalMedia.blurHash, textUrl = uploadLocalMedia.url diff --git a/src/main/kotlin/dev/usbharu/hideout/util/AcctUtil.kt b/src/main/kotlin/dev/usbharu/hideout/util/AcctUtil.kt index d8e7b246..a0f1a09b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/util/AcctUtil.kt +++ b/src/main/kotlin/dev/usbharu/hideout/util/AcctUtil.kt @@ -35,7 +35,7 @@ object AcctUtil { } else -> { - throw IllegalArgumentException("Invalid acct.(Too many @)") + throw IllegalArgumentException("Invalid acct. (Too many @)") } } } diff --git a/src/main/kotlin/dev/usbharu/hideout/util/HttpUtil.kt b/src/main/kotlin/dev/usbharu/hideout/util/HttpUtil.kt index 882a211f..66d321fc 100644 --- a/src/main/kotlin/dev/usbharu/hideout/util/HttpUtil.kt +++ b/src/main/kotlin/dev/usbharu/hideout/util/HttpUtil.kt @@ -3,10 +3,10 @@ package dev.usbharu.hideout.util import io.ktor.http.* object HttpUtil { - val ContentType.Application.Activity: ContentType + val Activity: ContentType get() = ContentType("application", "activity+json") - val ContentType.Application.JsonLd: ContentType + val JsonLd: ContentType get() { return ContentType( contentType = "application", From 259ff937dc19996d3b1a31bfa9ad7c3f31fe3220 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:34:56 +0900 Subject: [PATCH 040/116] =?UTF-8?q?refactor:=20object=E3=82=92apObject?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=A6=E3=82=A8=E3=82=B9=E3=82=B1=E3=83=BC?= =?UTF-8?q?=E3=83=97=E3=81=AE=E5=BF=85=E8=A6=81=E3=82=92=E3=81=AA=E3=81=8F?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/domain/model/Accept.kt | 24 +++++++++------ .../activitypub/domain/model/Create.kt | 30 ++++++++++++------- .../activitypub/domain/model/Delete.kt | 13 ++++---- .../activitypub/domain/model/Follow.kt | 9 +++--- .../hideout/activitypub/domain/model/Like.kt | 24 ++++++++++----- .../activity/accept/ApAcceptProcessor.kt | 4 +-- .../create/ApSendCreateServiceImpl.kt | 2 +- .../create/CreateActivityProcessor.kt | 2 +- .../activity/delete/APDeleteProcessor.kt | 2 +- .../activity/follow/APFollowProcessor.kt | 4 +-- .../follow/APReceiveFollowJobProcessor.kt | 2 +- .../activity/follow/APReceiveFollowService.kt | 4 +-- .../activity/follow/APSendFollowService.kt | 2 +- .../service/activity/like/APLikeProcessor.kt | 2 +- .../activity/like/ApReactionJobProcessor.kt | 2 +- .../service/activity/undo/APUndoProcessor.kt | 6 ++-- .../follow/APSendFollowServiceImplTest.kt | 2 +- .../common/APRequestServiceImplTest.kt | 16 +++++----- .../hideout/ap/ContextSerializerTest.kt | 4 +-- 19 files changed, 90 insertions(+), 64 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt index 15eaf20f..93ded2d2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.activitypub.domain.model import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer @@ -8,13 +9,13 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer open class Accept @JsonCreator constructor( type: List = emptyList(), override val name: String, - @JsonDeserialize(using = ObjectDeserializer::class) @Suppress("VariableNaming") var `object`: Object?, + @JsonDeserialize(using = ObjectDeserializer::class) + @JsonProperty("object") + val apObject: Object, override val actor: String ) : Object( type = add(type, "Accept") -), - HasActor, - HasName { +), HasActor, HasName { override fun equals(other: Any?): Boolean { if (this === other) return true @@ -23,22 +24,27 @@ open class Accept @JsonCreator constructor( other as Accept - if (`object` != other.`object`) return false - if (actor != other.actor) return false if (name != other.name) return false + if (apObject != other.apObject) return false + if (actor != other.actor) return false return true } override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (`object`?.hashCode() ?: 0) - result = 31 * result + actor.hashCode() result = 31 * result + name.hashCode() + result = 31 * result + apObject.hashCode() + result = 31 * result + actor.hashCode() return result } override fun toString(): String { - return "Accept(" + "`object`=$`object`, " + "actor='$actor', " + "name='$name'" + ")" + " ${super.toString()}" + return "Accept(" + + "name='$name', " + + "apObject=$apObject, " + + "actor='$actor'" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt index 6b6bb810..3353ea3c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.activitypub.domain.model +import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer @@ -8,8 +9,8 @@ open class Create( type: List = emptyList(), override val name: String, @JsonDeserialize(using = ObjectDeserializer::class) - @Suppress("VariableNaming") - val `object`: Object, + @JsonProperty("object") + val apObject: Object, override val actor: String, override val id: String, val to: List = emptyList(), @@ -28,27 +29,36 @@ open class Create( other as Create - if (`object` != other.`object`) return false - if (to != other.to) return false - if (cc != other.cc) return false if (name != other.name) return false + if (apObject != other.apObject) return false if (actor != other.actor) return false if (id != other.id) return false + if (to != other.to) return false + if (cc != other.cc) return false return true } override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (`object`?.hashCode() ?: 0) - result = 31 * result + to.hashCode() - result = 31 * result + cc.hashCode() result = 31 * result + name.hashCode() + result = 31 * result + apObject.hashCode() result = 31 * result + actor.hashCode() result = 31 * result + id.hashCode() + result = 31 * result + to.hashCode() + result = 31 * result + cc.hashCode() return result } - override fun toString(): String = - "Create(`object`=$`object`, to=$to, cc=$cc, name='$name', actor='$actor', id='$id') ${super.toString()}" + override fun toString(): String { + return "Create(" + + "name='$name', " + + "apObject=$apObject, " + + "actor='$actor', " + + "id='$id', " + + "to=$to, " + + "cc=$cc" + + ")" + + " ${super.toString()}" + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt index e4e8ccb0..f2142722 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt @@ -1,13 +1,14 @@ package dev.usbharu.hideout.activitypub.domain.model +import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer open class Delete : Object, HasId, HasActor { @JsonDeserialize(using = ObjectDeserializer::class) - @Suppress("VariableNaming") - val `object`: Object + @JsonProperty("object") + val apObject: Object val published: String override val actor: String override val id: String @@ -19,7 +20,7 @@ open class Delete : Object, HasId, HasActor { `object`: Object, published: String ) : super(add(type, "Delete")) { - this.`object` = `object` + this.apObject = `object` this.published = published this.actor = actor this.id = id @@ -32,7 +33,7 @@ open class Delete : Object, HasId, HasActor { other as Delete - if (`object` != other.`object`) return false + if (apObject != other.apObject) return false if (published != other.published) return false if (actor != other.actor) return false if (id != other.id) return false @@ -42,7 +43,7 @@ open class Delete : Object, HasId, HasActor { override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (`object`?.hashCode() ?: 0) + result = 31 * result + (apObject?.hashCode() ?: 0) result = 31 * result + (published?.hashCode() ?: 0) result = 31 * result + actor.hashCode() result = 31 * result + id.hashCode() @@ -50,5 +51,5 @@ open class Delete : Object, HasId, HasActor { } override fun toString(): String = - "Delete(`object`=$`object`, published=$published, actor='$actor', id='$id') ${super.toString()}" + "Delete(`object`=$apObject, published=$published, actor='$actor', id='$id') ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt index d4ef2c95..c7f292ba 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt @@ -1,10 +1,11 @@ package dev.usbharu.hideout.activitypub.domain.model +import com.fasterxml.jackson.annotation.JsonProperty import dev.usbharu.hideout.activitypub.domain.model.objects.Object open class Follow( type: List = emptyList(), - @Suppress("VariableNaming") val `object`: String, + @JsonProperty("object") val apObject: String, override val actor: String ) : Object( type = add(type, "Follow") @@ -18,7 +19,7 @@ open class Follow( other as Follow - if (`object` != other.`object`) return false + if (apObject != other.apObject) return false if (actor != other.actor) return false return true @@ -26,10 +27,10 @@ open class Follow( override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + `object`.hashCode() + result = 31 * result + apObject.hashCode() result = 31 * result + actor.hashCode() return result } - override fun toString(): String = "Follow(`object`=$`object`, actor='$actor') ${super.toString()}" + override fun toString(): String = "Follow(`object`=$apObject, actor='$actor') ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt index f4eaa13f..39ef65a2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.activitypub.domain.model +import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer @@ -8,7 +9,7 @@ open class Like( type: List = emptyList(), override val actor: String, override val id: String, - @Suppress("VariableNaming") val `object`: String, + @JsonProperty("object") val apObject: String, val content: String, @JsonDeserialize(contentUsing = ObjectDeserializer::class) val tag: List = emptyList() ) : Object( @@ -24,26 +25,33 @@ open class Like( other as Like - if (`object` != other.`object`) return false - if (content != other.content) return false - if (tag != other.tag) return false if (actor != other.actor) return false if (id != other.id) return false + if (apObject != other.apObject) return false + if (content != other.content) return false + if (tag != other.tag) return false return true } override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (`object`?.hashCode() ?: 0) - result = 31 * result + (content?.hashCode() ?: 0) - result = 31 * result + tag.hashCode() result = 31 * result + actor.hashCode() result = 31 * result + id.hashCode() + result = 31 * result + apObject.hashCode() + result = 31 * result + content.hashCode() + result = 31 * result + tag.hashCode() return result } override fun toString(): String { - return "Like(`object`=$`object`, content=$content, tag=$tag, actor='$actor', id='$id') ${super.toString()}" + return "Like(" + + "actor='$actor', " + + "id='$id', " + + "apObject='$apObject', " + + "content='$content', " + + "tag=$tag" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt index 0567bf5f..68538572 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt @@ -20,7 +20,7 @@ class ApAcceptProcessor( AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val value = activity.activity.`object` ?: throw IllegalActivityPubObjectException("object is null") + val value = activity.activity.apObject ?: throw IllegalActivityPubObjectException("object is null") if (value.type.contains("Follow").not()) { logger.warn("FAILED Activity type isn't Follow.") @@ -29,7 +29,7 @@ class ApAcceptProcessor( val follow = value as Follow - val userUrl = follow.`object` + val userUrl = follow.apObject val followerUrl = follow.actor val user = userQueryService.findByUrl(userUrl) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt index 0d3eeac8..226f2a63 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt @@ -33,7 +33,7 @@ class ApSendCreateServiceImpl( val note = noteQueryService.findById(post.id).first val create = Create( name = "Create Note", - `object` = note, + apObject = note, actor = note.attributedTo, id = "${applicationConfig.url}/create/note/${post.id}" ) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt index 2e1b557c..827042ef 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt @@ -13,7 +13,7 @@ import org.springframework.stereotype.Service class CreateActivityProcessor(transaction: Transaction, private val apNoteService: APNoteService) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { - apNoteService.fetchNote(activity.activity.`object` as Note) + apNoteService.fetchNote(activity.activity.apObject as Note) } override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Create diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt index f6f136fa..99f4b159 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt @@ -18,7 +18,7 @@ class APDeleteProcessor( ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val value = activity.activity.`object` + val value = activity.activity.apObject if (value !is HasId) { throw IllegalActivityPubObjectException("object hasn't id") } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt index b56cd49f..847f4609 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt @@ -17,13 +17,13 @@ class APFollowProcessor( ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { - logger.info("FOLLOW from: {} to {}", activity.activity.actor, activity.activity.`object`) + logger.info("FOLLOW from: {} to {}", activity.activity.actor, activity.activity.apObject) // inboxをジョブキューに乗せているので既に不要だが、フォロー承認制アカウントを実装する際に必要なので残す val jobProps = ReceiveFollowJobParam( activity.activity.actor, objectMapper.writeValueAsString(activity.activity), - activity.activity.`object` + activity.activity.apObject ) jobQueueParentService.scheduleTypeSafe(ReceiveFollowJob, jobProps) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt index f8cf8556..0c3957b1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt @@ -39,7 +39,7 @@ class APReceiveFollowJobProcessor( url = urlString, body = Accept( name = "Follow", - `object` = follow, + apObject = follow, actor = param.targetActor ), signer = signer diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt index 2011fce1..01d85a8b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt @@ -18,11 +18,11 @@ class APReceiveFollowServiceImpl( @Qualifier("activitypub") private val objectMapper: ObjectMapper ) : APReceiveFollowService { override suspend fun receiveFollow(follow: Follow) { - logger.info("FOLLOW from: {} to: {}", follow.actor, follow.`object`) + logger.info("FOLLOW from: {} to: {}", follow.actor, follow.apObject) jobQueueParentService.schedule(ReceiveFollowJob) { props[ReceiveFollowJob.actor] = follow.actor props[ReceiveFollowJob.follow] = objectMapper.writeValueAsString(follow) - props[ReceiveFollowJob.targetActor] = follow.`object` + props[ReceiveFollowJob.targetActor] = follow.apObject } return } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt index 554fa571..825ec198 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt @@ -15,7 +15,7 @@ class APSendFollowServiceImpl( ) : APSendFollowService { override suspend fun sendFollow(sendFollowDto: SendFollowDto) { val follow = Follow( - `object` = sendFollowDto.followTargetUserId.url, + apObject = sendFollowDto.followTargetUserId.url, actor = sendFollowDto.userId.url ) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt index 9d56fe94..8938f017 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt @@ -23,7 +23,7 @@ class APLikeProcessor( val actor = activity.activity.actor val content = activity.activity.content - val target = activity.activity.`object` + val target = activity.activity.apObject val personWithEntity = apUserService.fetchPersonWithEntity(actor) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt index 3f73bb2e..1fee5eda 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt @@ -22,7 +22,7 @@ class ApReactionJobProcessor( param.inbox, Like( actor = param.actor, - `object` = param.postUrl, + apObject = param.postUrl, id = "${applicationConfig.url}/liek/note/${param.id}", content = param.reaction ), diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt index 99bd9ba1..16419348 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt @@ -32,12 +32,12 @@ class APUndoProcessor( "Follow" -> { val follow = undo.`object` as Follow - if (follow.`object` == null) { + if (follow.apObject == null) { return } - apUserService.fetchPerson(undo.actor, follow.`object`) + apUserService.fetchPerson(undo.actor, follow.apObject) val follower = userQueryService.findByUrl(undo.actor) - val target = userQueryService.findByUrl(follow.`object`) + val target = userQueryService.findByUrl(follow.apObject) userService.unfollow(target.id, follower.id) return } diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt index 93e1d76d..0fe5f689 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt @@ -24,7 +24,7 @@ class APSendFollowServiceImplTest { apSendFollowServiceImpl.sendFollow(sendFollowDto) val value = Follow( - `object` = sendFollowDto.followTargetUserId.url, + apObject = sendFollowDto.followTargetUserId.url, actor = sendFollowDto.userId.url ) verify(apRequestService, times(1)).apPost( diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt index 2c609183..2bb41d56 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt @@ -47,7 +47,7 @@ class APRequestServiceImplTest { ) val responseClass = Follow( - `object` = "https://example.com", + apObject = "https://example.com", actor = "https://example.com" ) apRequestServiceImpl.apGet("https://example.com", responseClass = responseClass::class.java) @@ -72,7 +72,7 @@ class APRequestServiceImplTest { ) val responseClass = Follow( - `object` = "https://example.com", + apObject = "https://example.com", actor = "https://example.com" ) apRequestServiceImpl.apGet( @@ -112,7 +112,7 @@ class APRequestServiceImplTest { ) val responseClass = Follow( - `object` = "https://example.com", + apObject = "https://example.com", actor = "https://example.com" ) apRequestServiceImpl.apGet( @@ -163,7 +163,7 @@ class APRequestServiceImplTest { }), objectMapper, mock(), dateTimeFormatter) val body = Follow( - `object` = "https://example.com", + apObject = "https://example.com", actor = "https://example.com" ) apRequestServiceImpl.apPost("https://example.com", body, null) @@ -209,7 +209,7 @@ class APRequestServiceImplTest { }), objectMapper, mock(), dateTimeFormatter) val body = Follow( - `object` = "https://example.com", + apObject = "https://example.com", actor = "https://example.com" ) apRequestServiceImpl.apPost("https://example.com", body, null) @@ -239,7 +239,7 @@ class APRequestServiceImplTest { }), objectMapper, mock(), dateTimeFormatter) val body = Follow( - `object` = "https://example.com", + apObject = "https://example.com", actor = "https://example.com" ) apRequestServiceImpl.apPost("https://example.com", body, UserBuilder.remoteUserOf()) @@ -280,7 +280,7 @@ class APRequestServiceImplTest { }), objectMapper, httpSignatureSigner, dateTimeFormatter) val body = Follow( - `object` = "https://example.com", + apObject = "https://example.com", actor = "https://example.com" ) apRequestServiceImpl.apPost( @@ -330,7 +330,7 @@ class APRequestServiceImplTest { }), objectMapper, mock(), dateTimeFormatter) val body = Follow( - `object` = "https://example.com", + apObject = "https://example.com", actor = "https://example.com" ) val actual = apRequestServiceImpl.apPost("https://example.com", body, null, body::class.java) diff --git a/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt b/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt index d73e31d7..a141c11b 100644 --- a/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt @@ -12,8 +12,8 @@ class ContextSerializerTest { val accept = Accept( name = "aaa", actor = "bbb", - `object` = Follow( - `object` = "ddd", + apObject = Follow( + apObject = "ddd", actor = "aaa" ) ) From c28b1ab11ef0fdccbc5d4a416ac0a07728e2fb01 Mon Sep 17 00:00:00 2001 From: usbharu Date: Tue, 28 Nov 2023 12:41:26 +0900 Subject: [PATCH 041/116] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../activitypub/domain/model/Accept.kt | 14 +++++++----- .../activitypub/domain/model/Create.kt | 16 +++++++------- .../hideout/activitypub/domain/model/Like.kt | 14 ++++++------ .../hideout/activitypub/domain/model/Note.kt | 22 +++++++++---------- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt index 93ded2d2..2cd730db 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt @@ -15,7 +15,9 @@ open class Accept @JsonCreator constructor( override val actor: String ) : Object( type = add(type, "Accept") -), HasActor, HasName { +), + HasActor, + HasName { override fun equals(other: Any?): Boolean { if (this === other) return true @@ -41,10 +43,10 @@ open class Accept @JsonCreator constructor( override fun toString(): String { return "Accept(" + - "name='$name', " + - "apObject=$apObject, " + - "actor='$actor'" + - ")" + - " ${super.toString()}" + "name='$name', " + + "apObject=$apObject, " + + "actor='$actor'" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt index 3353ea3c..d5b269dd 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt @@ -52,13 +52,13 @@ open class Create( override fun toString(): String { return "Create(" + - "name='$name', " + - "apObject=$apObject, " + - "actor='$actor', " + - "id='$id', " + - "to=$to, " + - "cc=$cc" + - ")" + - " ${super.toString()}" + "name='$name', " + + "apObject=$apObject, " + + "actor='$actor', " + + "id='$id', " + + "to=$to, " + + "cc=$cc" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt index 39ef65a2..ec8e8ec7 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt @@ -46,12 +46,12 @@ open class Like( override fun toString(): String { return "Like(" + - "actor='$actor', " + - "id='$id', " + - "apObject='$apObject', " + - "content='$content', " + - "tag=$tag" + - ")" + - " ${super.toString()}" + "actor='$actor', " + + "id='$id', " + + "apObject='$apObject', " + + "content='$content', " + + "tag=$tag" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt index 91bf8a86..2b16e1c4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt @@ -56,16 +56,16 @@ constructor( override fun toString(): String { return "Note(" + - "id='$id', " + - "attributedTo='$attributedTo', " + - "content='$content', " + - "published='$published', " + - "to=$to, " + - "cc=$cc, " + - "sensitive=$sensitive, " + - "inReplyTo=$inReplyTo, " + - "attachment=$attachment" + - ")" + - " ${super.toString()}" + "id='$id', " + + "attributedTo='$attributedTo', " + + "content='$content', " + + "published='$published', " + + "to=$to, " + + "cc=$cc, " + + "sensitive=$sensitive, " + + "inReplyTo=$inReplyTo, " + + "attachment=$attachment" + + ")" + + " ${super.toString()}" } } From 820364813f0e7d188f273c311a9abb497269f6f6 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:24:01 +0900 Subject: [PATCH 042/116] =?UTF-8?q?feat:=20=E7=99=BB=E9=8C=B2=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=81=A6=E3=81=84=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F?= =?UTF-8?q?JobProcessor=E3=82=84ActivityPubProcessor=E3=82=92=E7=99=BB?= =?UTF-8?q?=E9=8C=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity/accept/ApAcceptProcessor.kt | 2 + .../activity/delete/APDeleteProcessor.kt | 2 + .../activity/follow/APFollowProcessor.kt | 2 + .../service/activity/like/APLikeProcessor.kt | 2 + .../activity/like/ApReactionJobProcessor.kt | 2 + .../like/ApRemoveReactionJobProcessor.kt | 2 + .../service/activity/undo/APUndoProcessor.kt | 2 + .../common/AbstractActivityPubProcessor.kt | 2 + .../service/inbox/InboxJobProcessor.kt | 43 ++----------------- .../objects/note/ApNoteJobProcessor.kt | 2 + 10 files changed, 21 insertions(+), 40 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt index 68538572..2ee0f07e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt @@ -10,7 +10,9 @@ import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.query.FollowerQueryService import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.user.UserService +import org.springframework.stereotype.Service +@Service class ApAcceptProcessor( transaction: Transaction, private val userQueryService: UserQueryService, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt index 99f4b159..efb37938 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt @@ -10,7 +10,9 @@ import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.query.PostQueryService +import org.springframework.stereotype.Service +@Service class APDeleteProcessor( transaction: Transaction, private val postQueryService: PostQueryService, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt index 847f4609..537606f4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt @@ -9,7 +9,9 @@ import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.external.job.ReceiveFollowJob import dev.usbharu.hideout.core.external.job.ReceiveFollowJobParam import dev.usbharu.hideout.core.service.job.JobQueueParentService +import org.springframework.stereotype.Service +@Service class APFollowProcessor( transaction: Transaction, private val jobQueueParentService: JobQueueParentService, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt index 8938f017..665c7c94 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt @@ -10,7 +10,9 @@ import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.query.PostQueryService import dev.usbharu.hideout.core.service.reaction.ReactionService +import org.springframework.stereotype.Service +@Service class APLikeProcessor( transaction: Transaction, private val apUserService: APUserService, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt index 1fee5eda..1487eb56 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt @@ -8,7 +8,9 @@ import dev.usbharu.hideout.core.external.job.DeliverReactionJob import dev.usbharu.hideout.core.external.job.DeliverReactionJobParam import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.job.JobProcessor +import org.springframework.stereotype.Service +@Service class ApReactionJobProcessor( private val userQueryService: UserQueryService, private val apRequestService: APRequestService, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt index 6a873aca..285670b5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt @@ -11,8 +11,10 @@ import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJob import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJobParam import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.job.JobProcessor +import org.springframework.stereotype.Service import java.time.Instant +@Service class ApRemoveReactionJobProcessor( private val userQueryService: UserQueryService, private val transaction: Transaction, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt index 16419348..2c8067a4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt @@ -9,7 +9,9 @@ import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.user.UserService +import org.springframework.stereotype.Service +@Service class APUndoProcessor( transaction: Transaction, private val apUserService: APUserService, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt index 1bb106e3..0e04262e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt @@ -7,7 +7,9 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.application.external.Transaction import org.slf4j.Logger import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +@Service abstract class AbstractActivityPubProcessor( private val transaction: Transaction, private val allowUnauthorized: Boolean = false diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt index fc68f7fa..1e4aeb2d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt @@ -5,7 +5,6 @@ import com.fasterxml.jackson.module.kotlin.readValue import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException @@ -20,13 +19,12 @@ import dev.usbharu.httpsignature.common.PublicKey import dev.usbharu.httpsignature.verify.HttpSignatureVerifier import dev.usbharu.httpsignature.verify.Signature import dev.usbharu.httpsignature.verify.SignatureHeaderParser -import kjob.core.job.JobProps import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service class InboxJobProcessor( - private val activityPubProcessorList: List>, + private val activityPubProcessorList: List>, private val objectMapper: ObjectMapper, private val signatureHeaderParser: SignatureHeaderParser, private val signatureVerifier: HttpSignatureVerifier, @@ -34,42 +32,6 @@ class InboxJobProcessor( private val apUserService: APUserService, private val transaction: Transaction ) : JobProcessor { - suspend fun process(props: JobProps) { - val type = ActivityType.valueOf(props[InboxJob.type]) - val jsonString = objectMapper.readTree(props[InboxJob.json]) - val httpRequestString = props[InboxJob.httpRequest] - val headersString = props[InboxJob.headers] - - logger.info("START Process inbox. type: {}", type) - logger.trace("type: {} \njson: \n{}", type, jsonString.toPrettyString()) - - val map = objectMapper.readValue>>(headersString) - - val httpRequest = - objectMapper.readValue(httpRequestString).copy(headers = HttpHeaders(map)) - - logger.trace("request: {}\nheaders: {}", httpRequest, map) - - val signature = parseSignatureHeader(httpRequest.headers) - - logger.debug("Has signature? {}", signature != null) - - val verify = signature?.let { verifyHttpSignature(httpRequest, it) } ?: false - - logger.debug("Is verifying success? {}", verify) - - val activityPubProcessor = activityPubProcessorList.firstOrNull { it.isSupported(type) } - - if (activityPubProcessor == null) { - logger.warn("ActivityType {} is not support.", type) - throw IllegalStateException("ActivityPubProcessor not found.") - } - - val value = objectMapper.treeToValue(jsonString, activityPubProcessor.type()) - activityPubProcessor.process(ActivityPubProcessContext(value, jsonString, httpRequest, signature, verify)) - - logger.info("SUCCESS Process inbox. type: {}", type) - } private suspend fun verifyHttpSignature(httpRequest: HttpRequest, signature: Signature): Boolean { val user = try { @@ -116,7 +78,8 @@ class InboxJobProcessor( logger.debug("Is verifying success? {}", verify) - val activityPubProcessor = activityPubProcessorList.firstOrNull { it.isSupported(param.type) } + val activityPubProcessor = + activityPubProcessorList.firstOrNull { it.isSupported(param.type) } as ActivityPubProcessor? if (activityPubProcessor == null) { logger.warn("ActivityType {} is not support.", param.type) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobProcessor.kt index df54350d..181f869a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobProcessor.kt @@ -10,7 +10,9 @@ import dev.usbharu.hideout.core.external.job.DeliverPostJobParam import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +@Service class ApNoteJobProcessor( private val transaction: Transaction, private val objectMapper: ObjectMapper, From 631acc534e1df5e7472eef8ea4dc00b46acd6446 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:34:21 +0900 Subject: [PATCH 043/116] =?UTF-8?q?feat:=20http-signature=E3=81=AESpring?= =?UTF-8?q?=20Security=E3=82=92=E4=BD=BF=E7=94=A8=E3=81=97=E3=81=9F?= =?UTF-8?q?=E8=AA=8D=E8=A8=BC=E3=82=92post=E3=81=AE=E3=81=BF=E3=81=AB?= =?UTF-8?q?=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/usbharu/hideout/application/config/SecurityConfig.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index 089a545a..dece6e79 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -81,7 +81,7 @@ class SecurityConfig { ): SecurityFilterChain { val builder = MvcRequestMatcher.Builder(introspector) http - .securityMatcher("/inbox", "/outbox", "/users/*/inbox", "/users/*/outbox", "/users/*/posts/*") + .securityMatcher("/users/*/posts/*") .addFilter(httpSignatureFilter) .addFilterBefore( ExceptionTranslationFilter(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)), From cc046393d6b62d4b6a4c06879e44bd5d4f941687 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:56:39 +0900 Subject: [PATCH 044/116] =?UTF-8?q?feat:=20Signature=E3=83=98=E3=83=83?= =?UTF-8?q?=E3=83=80=E3=83=BC=E3=81=8C=E3=81=AA=E3=81=84=E5=A0=B4=E5=90=88?= =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=88=E3=83=AD=E3=83=BC=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E3=81=AE=E6=99=82=E7=82=B9=E3=81=A7401=E3=82=92=E8=BF=94?= =?UTF-8?q?=E3=81=99=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/intTest/kotlin/activitypub/inbox/InboxTest.kt | 2 ++ .../interfaces/api/inbox/InboxControllerImpl.kt | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/intTest/kotlin/activitypub/inbox/InboxTest.kt b/src/intTest/kotlin/activitypub/inbox/InboxTest.kt index 92fe3aa2..4626772c 100644 --- a/src/intTest/kotlin/activitypub/inbox/InboxTest.kt +++ b/src/intTest/kotlin/activitypub/inbox/InboxTest.kt @@ -45,6 +45,7 @@ class InboxTest { content = "{}" contentType = MediaType.APPLICATION_JSON } + .asyncDispatch() .andExpect { status { isUnauthorized() } } } @@ -68,6 +69,7 @@ class InboxTest { content = "{}" contentType = MediaType.APPLICATION_JSON } + .asyncDispatch() .andExpect { status { isUnauthorized() } } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt index 1ad9062c..6c47a5b1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt @@ -5,6 +5,7 @@ import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest import org.slf4j.LoggerFactory +import org.springframework.http.HttpHeaders.WWW_AUTHENTICATE import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.RequestBody @@ -21,6 +22,16 @@ class InboxControllerImpl(private val apService: APService) : InboxController { ): ResponseEntity { val request = (requireNotNull(RequestContextHolder.getRequestAttributes()) as ServletRequestAttributes).request + val headersList = request.headerNames?.toList().orEmpty() + if (headersList.contains("Signature").not()) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .header( + WWW_AUTHENTICATE, + "Signature realm=\"Example\",headers=\"(request-target) date host digest\"" + ) + .build() + } + val parseActivity = try { apService.parseActivity(string) } catch (e: Exception) { @@ -31,7 +42,7 @@ class InboxControllerImpl(private val apService: APService) : InboxController { try { val url = request.requestURL.toString() - val headersList = request.headerNames?.toList().orEmpty() + val headers = headersList.associateWith { header -> request.getHeaders(header)?.toList().orEmpty() } @@ -43,8 +54,6 @@ class InboxControllerImpl(private val apService: APService) : InboxController { } } - println(headers) - apService.processActivity( string, parseActivity, From f0366ec5bae50ebd610d7d31009feb314d76c7c9 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:18:51 +0900 Subject: [PATCH 045/116] =?UTF-8?q?feat:=20HTTP=20Signature=E3=81=A7?= =?UTF-8?q?=E5=BF=85=E8=A6=81=E3=81=AA=E3=83=98=E3=83=83=E3=83=80=E3=83=BC?= =?UTF-8?q?=E3=81=8C=E5=90=AB=E3=81=BE=E3=82=8C=E3=81=A6=E3=81=84=E3=82=8B?= =?UTF-8?q?=E3=81=8B=E3=82=92=E6=A4=9C=E8=A8=BC=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/inbox/InboxJobProcessor.kt | 12 +++++++++ .../application/config/SecurityConfig.kt | 2 +- .../httpsignature/HttpSignatureFilter.kt | 19 +------------ .../HttpSignatureUserDetailsService.kt | 27 ++++++++++++++++--- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt index 1e4aeb2d..d1bebb2b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt @@ -14,6 +14,7 @@ import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import dev.usbharu.hideout.util.RsaUtil import dev.usbharu.httpsignature.common.HttpHeaders +import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest import dev.usbharu.httpsignature.common.PublicKey import dev.usbharu.httpsignature.verify.HttpSignatureVerifier @@ -34,6 +35,15 @@ class InboxJobProcessor( ) : JobProcessor { private suspend fun verifyHttpSignature(httpRequest: HttpRequest, signature: Signature): Boolean { + + val requiredHeaders = when (httpRequest.method) { + HttpMethod.GET -> getRequiredHeaders + HttpMethod.POST -> postRequiredHeaders + } + if (signature.headers.containsAll(requiredHeaders).not()) { + return false + } + val user = try { userQueryService.findByKeyId(signature.keyId) } catch (_: FailedToGetResourcesException) { @@ -96,5 +106,7 @@ class InboxJobProcessor( companion object { private val logger = LoggerFactory.getLogger(InboxJobProcessor::class.java) + private val postRequiredHeaders = listOf("(request-target)", "date", "host", "digest") + private val getRequiredHeaders = listOf("(request-target)", "date", "host") } } diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index dece6e79..84df4aa3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -121,7 +121,7 @@ class SecurityConfig { userQueryService: UserQueryService ): HttpSignatureFilter { val httpSignatureFilter = - HttpSignatureFilter(DefaultSignatureHeaderParser(), transaction, apUserService, userQueryService) + HttpSignatureFilter(DefaultSignatureHeaderParser()) httpSignatureFilter.setAuthenticationManager(authenticationManager) httpSignatureFilter.setContinueFilterChainOnUnsuccessfulAuthentication(false) val authenticationEntryPointFailureHandler = diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt index e814e568..d4332651 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt @@ -1,23 +1,15 @@ package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest import dev.usbharu.httpsignature.verify.SignatureHeaderParser import jakarta.servlet.http.HttpServletRequest -import kotlinx.coroutines.runBlocking import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter import java.net.URL class HttpSignatureFilter( - private val httpSignatureHeaderParser: SignatureHeaderParser, - private val transaction: Transaction, - private val apUserService: APUserService, - private val userQueryService: UserQueryService + private val httpSignatureHeaderParser: SignatureHeaderParser ) : AbstractPreAuthenticatedProcessingFilter() { override fun getPreAuthenticatedPrincipal(request: HttpServletRequest?): Any? { @@ -33,15 +25,6 @@ class HttpSignatureFilter( } catch (_: RuntimeException) { return "" } - runBlocking { - transaction.transaction { - try { - userQueryService.findByKeyId(signature.keyId) - } catch (_: FailedToGetResourcesException) { - apUserService.fetchPerson(signature.keyId) - } - } - } return signature.keyId } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt index a2e2a258..3acc12f6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt @@ -5,10 +5,12 @@ import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.exception.HttpSignatureVerifyException import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.util.RsaUtil +import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest import dev.usbharu.httpsignature.common.PublicKey import dev.usbharu.httpsignature.verify.FailedVerification import dev.usbharu.httpsignature.verify.HttpSignatureVerifier +import dev.usbharu.httpsignature.verify.SignatureHeaderParser import kotlinx.coroutines.runBlocking import org.slf4j.LoggerFactory import org.springframework.security.authentication.BadCredentialsException @@ -20,14 +22,16 @@ import org.springframework.security.web.authentication.preauth.PreAuthenticatedA class HttpSignatureUserDetailsService( private val userQueryService: UserQueryService, private val httpSignatureVerifier: HttpSignatureVerifier, - private val transaction: Transaction + private val transaction: Transaction, + private val httpSignatureHeaderParser: SignatureHeaderParser ) : AuthenticationUserDetailsService { override fun loadUserDetails(token: PreAuthenticatedAuthenticationToken): UserDetails = runBlocking { if (token.principal !is String) { throw IllegalStateException("Token is not String") } - if (token.credentials !is HttpRequest) { + val credentials = token.credentials + if (credentials !is HttpRequest) { throw IllegalStateException("Credentials is not HttpRequest") } @@ -40,10 +44,25 @@ class HttpSignatureUserDetailsService( } } + val signature = httpSignatureHeaderParser.parse(credentials.headers) + + val requiredHeaders = when (credentials.method) { + HttpMethod.GET -> getRequiredHeaders + HttpMethod.POST -> postRequiredHeaders + } + if (signature.headers.containsAll(requiredHeaders).not()) { + logger.warn( + "FAILED Verify HTTP Signature. required headers: {} but actual: {}", + requiredHeaders, + signature.headers + ) + throw BadCredentialsException("HTTP Signature. required headers: $requiredHeaders") + } + @Suppress("TooGenericExceptionCaught") val verify = try { httpSignatureVerifier.verify( - token.credentials as HttpRequest, + credentials, PublicKey(RsaUtil.decodeRsaPublicKeyPem(findByKeyId.publicKey), keyId) ) } catch (e: RuntimeException) { @@ -67,5 +86,7 @@ class HttpSignatureUserDetailsService( companion object { private val logger = LoggerFactory.getLogger(HttpSignatureUserDetailsService::class.java) + private val postRequiredHeaders = listOf("(request-target)", "date", "host", "digest") + private val getRequiredHeaders = listOf("(request-target)", "date", "host") } } From b1356e849694514006896313563a3a2e6fb1c554 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:18:51 +0900 Subject: [PATCH 046/116] =?UTF-8?q?test:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/activitypub/inbox/InboxTest.kt | 2 ++ .../api/inbox/InboxControllerImpl.kt | 1 - .../service/inbox/InboxJobProcessor.kt | 11 ++++++++ .../application/config/SecurityConfig.kt | 10 ++++--- .../httpsignature/HttpSignatureFilter.kt | 19 +------------ .../HttpSignatureUserDetailsService.kt | 27 ++++++++++++++++--- .../api/inbox/InboxControllerImplTest.kt | 6 +++++ 7 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/intTest/kotlin/activitypub/inbox/InboxTest.kt b/src/intTest/kotlin/activitypub/inbox/InboxTest.kt index 4626772c..080639b4 100644 --- a/src/intTest/kotlin/activitypub/inbox/InboxTest.kt +++ b/src/intTest/kotlin/activitypub/inbox/InboxTest.kt @@ -56,6 +56,7 @@ class InboxTest { .post("/inbox") { content = "{}" contentType = MediaType.APPLICATION_JSON + header("Signature", "") } .asyncDispatch() .andExpect { status { isAccepted() } } @@ -80,6 +81,7 @@ class InboxTest { .post("/users/hoge/inbox") { content = "{}" contentType = MediaType.APPLICATION_JSON + header("Signature", "") } .asyncDispatch() .andExpect { status { isAccepted() } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt index 6c47a5b1..d87d045a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt @@ -42,7 +42,6 @@ class InboxControllerImpl(private val apService: APService) : InboxController { try { val url = request.requestURL.toString() - val headers = headersList.associateWith { header -> request.getHeaders(header)?.toList().orEmpty() } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt index 1e4aeb2d..1ca31144 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt @@ -14,6 +14,7 @@ import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import dev.usbharu.hideout.util.RsaUtil import dev.usbharu.httpsignature.common.HttpHeaders +import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest import dev.usbharu.httpsignature.common.PublicKey import dev.usbharu.httpsignature.verify.HttpSignatureVerifier @@ -34,6 +35,14 @@ class InboxJobProcessor( ) : JobProcessor { private suspend fun verifyHttpSignature(httpRequest: HttpRequest, signature: Signature): Boolean { + val requiredHeaders = when (httpRequest.method) { + HttpMethod.GET -> getRequiredHeaders + HttpMethod.POST -> postRequiredHeaders + } + if (signature.headers.containsAll(requiredHeaders).not()) { + return false + } + val user = try { userQueryService.findByKeyId(signature.keyId) } catch (_: FailedToGetResourcesException) { @@ -96,5 +105,7 @@ class InboxJobProcessor( companion object { private val logger = LoggerFactory.getLogger(InboxJobProcessor::class.java) + private val postRequiredHeaders = listOf("(request-target)", "date", "host", "digest") + private val getRequiredHeaders = listOf("(request-target)", "date", "host") } } diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index dece6e79..499e68dd 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -121,7 +121,7 @@ class SecurityConfig { userQueryService: UserQueryService ): HttpSignatureFilter { val httpSignatureFilter = - HttpSignatureFilter(DefaultSignatureHeaderParser(), transaction, apUserService, userQueryService) + HttpSignatureFilter(DefaultSignatureHeaderParser()) httpSignatureFilter.setAuthenticationManager(authenticationManager) httpSignatureFilter.setContinueFilterChainOnUnsuccessfulAuthentication(false) val authenticationEntryPointFailureHandler = @@ -134,18 +134,20 @@ class SecurityConfig { @Bean fun httpSignatureAuthenticationProvider(transaction: Transaction): PreAuthenticatedAuthenticationProvider { val provider = PreAuthenticatedAuthenticationProvider() + val signatureHeaderParser = DefaultSignatureHeaderParser() provider.setPreAuthenticatedUserDetailsService( HttpSignatureUserDetailsService( userQueryService, HttpSignatureVerifierComposite( mapOf( "rsa-sha256" to RsaSha256HttpSignatureVerifier( - DefaultSignatureHeaderParser(), RsaSha256HttpSignatureSigner() + signatureHeaderParser, RsaSha256HttpSignatureSigner() ) ), - DefaultSignatureHeaderParser() + signatureHeaderParser ), - transaction + transaction, + signatureHeaderParser ) ) provider.setUserDetailsChecker(AccountStatusUserDetailsChecker()) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt index e814e568..d4332651 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt @@ -1,23 +1,15 @@ package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest import dev.usbharu.httpsignature.verify.SignatureHeaderParser import jakarta.servlet.http.HttpServletRequest -import kotlinx.coroutines.runBlocking import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter import java.net.URL class HttpSignatureFilter( - private val httpSignatureHeaderParser: SignatureHeaderParser, - private val transaction: Transaction, - private val apUserService: APUserService, - private val userQueryService: UserQueryService + private val httpSignatureHeaderParser: SignatureHeaderParser ) : AbstractPreAuthenticatedProcessingFilter() { override fun getPreAuthenticatedPrincipal(request: HttpServletRequest?): Any? { @@ -33,15 +25,6 @@ class HttpSignatureFilter( } catch (_: RuntimeException) { return "" } - runBlocking { - transaction.transaction { - try { - userQueryService.findByKeyId(signature.keyId) - } catch (_: FailedToGetResourcesException) { - apUserService.fetchPerson(signature.keyId) - } - } - } return signature.keyId } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt index a2e2a258..3acc12f6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt @@ -5,10 +5,12 @@ import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.exception.HttpSignatureVerifyException import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.util.RsaUtil +import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest import dev.usbharu.httpsignature.common.PublicKey import dev.usbharu.httpsignature.verify.FailedVerification import dev.usbharu.httpsignature.verify.HttpSignatureVerifier +import dev.usbharu.httpsignature.verify.SignatureHeaderParser import kotlinx.coroutines.runBlocking import org.slf4j.LoggerFactory import org.springframework.security.authentication.BadCredentialsException @@ -20,14 +22,16 @@ import org.springframework.security.web.authentication.preauth.PreAuthenticatedA class HttpSignatureUserDetailsService( private val userQueryService: UserQueryService, private val httpSignatureVerifier: HttpSignatureVerifier, - private val transaction: Transaction + private val transaction: Transaction, + private val httpSignatureHeaderParser: SignatureHeaderParser ) : AuthenticationUserDetailsService { override fun loadUserDetails(token: PreAuthenticatedAuthenticationToken): UserDetails = runBlocking { if (token.principal !is String) { throw IllegalStateException("Token is not String") } - if (token.credentials !is HttpRequest) { + val credentials = token.credentials + if (credentials !is HttpRequest) { throw IllegalStateException("Credentials is not HttpRequest") } @@ -40,10 +44,25 @@ class HttpSignatureUserDetailsService( } } + val signature = httpSignatureHeaderParser.parse(credentials.headers) + + val requiredHeaders = when (credentials.method) { + HttpMethod.GET -> getRequiredHeaders + HttpMethod.POST -> postRequiredHeaders + } + if (signature.headers.containsAll(requiredHeaders).not()) { + logger.warn( + "FAILED Verify HTTP Signature. required headers: {} but actual: {}", + requiredHeaders, + signature.headers + ) + throw BadCredentialsException("HTTP Signature. required headers: $requiredHeaders") + } + @Suppress("TooGenericExceptionCaught") val verify = try { httpSignatureVerifier.verify( - token.credentials as HttpRequest, + credentials, PublicKey(RsaUtil.decodeRsaPublicKeyPem(findByKeyId.publicKey), keyId) ) } catch (e: RuntimeException) { @@ -67,5 +86,7 @@ class HttpSignatureUserDetailsService( companion object { private val logger = LoggerFactory.getLogger(HttpSignatureUserDetailsService::class.java) + private val postRequiredHeaders = listOf("(request-target)", "date", "host", "digest") + private val getRequiredHeaders = listOf("(request-target)", "date", "host") } } diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt index 4fe4a3b2..b1f0b0cc 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt @@ -54,6 +54,7 @@ class InboxControllerImplTest { .post("/inbox") { content = json contentType = MediaType.APPLICATION_JSON + header("Signature", "") } .asyncDispatch() .andExpect { @@ -71,6 +72,7 @@ class InboxControllerImplTest { .post("/inbox") { content = json contentType = MediaType.APPLICATION_JSON + header("Signature", "") } .asyncDispatch() .andExpect { @@ -96,6 +98,7 @@ class InboxControllerImplTest { .post("/inbox") { content = json contentType = MediaType.APPLICATION_JSON + header("Signature", "") } .asyncDispatch() .andExpect { @@ -123,6 +126,7 @@ class InboxControllerImplTest { .post("/users/hoge/inbox") { content = json contentType = MediaType.APPLICATION_JSON + header("Signature", "") } .asyncDispatch() .andExpect { @@ -140,6 +144,7 @@ class InboxControllerImplTest { .post("/users/hoge/inbox") { content = json contentType = MediaType.APPLICATION_JSON + header("Signature", "") } .asyncDispatch() .andExpect { @@ -165,6 +170,7 @@ class InboxControllerImplTest { .post("/users/hoge/inbox") { content = json contentType = MediaType.APPLICATION_JSON + header("Signature", "") } .asyncDispatch() .andExpect { From 9c24c736ec633285a839596917ae3bee79eda58e Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 29 Nov 2023 12:48:56 +0900 Subject: [PATCH 047/116] =?UTF-8?q?test:=20account=20api=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/mastodon/account/AccountApiTest.kt | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/intTest/kotlin/mastodon/account/AccountApiTest.kt diff --git a/src/intTest/kotlin/mastodon/account/AccountApiTest.kt b/src/intTest/kotlin/mastodon/account/AccountApiTest.kt new file mode 100644 index 00000000..88168a51 --- /dev/null +++ b/src/intTest/kotlin/mastodon/account/AccountApiTest.kt @@ -0,0 +1,136 @@ +package mastodon.account + +import dev.usbharu.hideout.SpringApplication +import dev.usbharu.hideout.core.infrastructure.exposedquery.UserQueryServiceImpl +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.MediaType +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.security.test.context.support.WithAnonymousUser +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity +import org.springframework.test.context.jdbc.Sql +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.get +import org.springframework.test.web.servlet.post +import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder +import org.springframework.test.web.servlet.setup.MockMvcBuilders +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.context.WebApplicationContext + +@SpringBootTest(classes = [SpringApplication::class]) +@AutoConfigureMockMvc +@Transactional +@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +class AccountApiTest { + + @Autowired + private lateinit var userQueryServiceImpl: UserQueryServiceImpl + + @Autowired + private lateinit var context: WebApplicationContext + + private lateinit var mockMvc: MockMvc + + + @BeforeEach + fun setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(context) + .apply(springSecurity()) + .build() + } + + @Test + fun `apiV1AccountsVerifyCredentialsGetにreadでアクセスできる`() { + mockMvc + .get("/api/v1/accounts/verify_credentials") { + with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))) + } + .asyncDispatch() + .andDo { print() } + .andExpect { status { isOk() } } + } + + @Test + fun `apiV1AccountsVerifyCredentialsGetにread_accountsでアクセスできる`() { + mockMvc + .get("/api/v1/accounts/verify_credentials") { + with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:accounts"))) + } + .asyncDispatch() + .andDo { print() } + .andExpect { status { isOk() } } + } + + @Test + @WithAnonymousUser + fun apiV1AccountsVerifyCredentialsGetに匿名でアクセスすると401() { + mockMvc + .get("/api/v1/accounts/verify_credentials") + .andExpect { status { isUnauthorized() } } + } + + @Test + @WithAnonymousUser + fun apiV1AccountsPostに匿名でPOSTしたらアカウントを作成できる() = runTest { + mockMvc + .post("/api/v1/accounts") { + contentType = MediaType.APPLICATION_FORM_URLENCODED + param("username", "api-test-user-1") + param("password", "very-secure-password") + param("email", "test@example.com") + param("agreement", "true") + param("locale", "") + with(SecurityMockMvcRequestPostProcessors.csrf()) + } + .asyncDispatch() + .andExpect { status { isFound() } } + + userQueryServiceImpl.findByNameAndDomain("api-test-user-1", "localhost") + } + + @Test + @WithAnonymousUser + fun apiV1AccountsPostで必須パラメーター以外を省略しても作成できる() = runTest { + mockMvc + .post("/api/v1/accounts") { + contentType = MediaType.APPLICATION_FORM_URLENCODED + param("username", "api-test-user-2") + param("password", "very-secure-password") + with(SecurityMockMvcRequestPostProcessors.csrf()) + } + .asyncDispatch() + .andExpect { status { isFound() } } + + userQueryServiceImpl.findByNameAndDomain("api-test-user-2", "localhost") + } + + @Test + @WithAnonymousUser + fun apiV1AccountsPostでusernameパラメーターを省略したら400() = runTest { + mockMvc + .post("/api/v1/accounts") { + contentType = MediaType.APPLICATION_FORM_URLENCODED + param("username", "api-test-user-3") + with(SecurityMockMvcRequestPostProcessors.csrf()) + } + .andExpect { status { isBadRequest() } } + } + + @Test + @WithAnonymousUser + fun apiV1AccountsPostでpasswordパラメーターを省略したら400() = runTest { + mockMvc + .post("/api/v1/accounts") { + contentType = MediaType.APPLICATION_FORM_URLENCODED + param("username", "api-test-user-3") + with(SecurityMockMvcRequestPostProcessors.csrf()) + } + .andExpect { status { isBadRequest() } } + } +} From fcb3d39881b0bab1885b0a3191f4ea1ff8a5b4f4 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 29 Nov 2023 12:49:18 +0900 Subject: [PATCH 048/116] =?UTF-8?q?test:=20apps=20api=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 4 +- gradle.properties | 2 +- src/intTest/kotlin/mastodon/apps/AppTest.kt | 88 +++++++++++++++++++++ src/intTest/resources/application.yml | 10 +-- 4 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 src/intTest/kotlin/mastodon/apps/AppTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index b5f5cb83..a17c93a4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ plugins { kotlin("jvm") version "1.8.21" id("org.graalvm.buildtools.native") version "0.9.21" id("io.gitlab.arturbosch.detekt") version "1.23.1" - id("org.springframework.boot") version "3.1.3" + id("org.springframework.boot") version "3.2.0" kotlin("plugin.spring") version "1.8.21" id("org.openapi.generator") version "7.0.1" id("org.jetbrains.kotlinx.kover") version "0.7.4" @@ -206,6 +206,8 @@ dependencies { intTestImplementation("org.springframework.boot:spring-boot-starter-test") intTestImplementation("org.springframework.security:spring-security-test") + intTestImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version") + intTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4") } diff --git a/gradle.properties b/gradle.properties index a7ffe13f..5516464a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ ktor_version=2.3.0 -kotlin_version=1.8.21 +kotlin_version=1.9.21 logback_version=1.4.6 kotlin.code.style=official exposed_version=0.44.0 diff --git a/src/intTest/kotlin/mastodon/apps/AppTest.kt b/src/intTest/kotlin/mastodon/apps/AppTest.kt new file mode 100644 index 00000000..f835d3be --- /dev/null +++ b/src/intTest/kotlin/mastodon/apps/AppTest.kt @@ -0,0 +1,88 @@ +package mastodon.apps + +import dev.usbharu.hideout.SpringApplication +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.RegisteredClient +import org.assertj.core.api.Assertions.assertThat +import org.jetbrains.exposed.sql.select +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.MediaType +import org.springframework.security.test.context.support.WithAnonymousUser +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.post +import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder +import org.springframework.test.web.servlet.setup.MockMvcBuilders +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.context.WebApplicationContext + +@SpringBootTest(classes = [SpringApplication::class]) +@AutoConfigureMockMvc +@Transactional +class AppTest { + + @Autowired + private lateinit var context: WebApplicationContext + + private lateinit var mockMvc: MockMvc + + + @BeforeEach + fun setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(context) + .apply(SecurityMockMvcConfigurers.springSecurity()) + .build() + } + + @Test + @WithAnonymousUser + fun apiV1AppsPostにformで匿名でappを作成できる() { + mockMvc + .post("/api/v1/apps") { + contentType = MediaType.APPLICATION_FORM_URLENCODED + param("client_name", "test-client") + param("redirect_uris", "https://example.com") + param("scopes", "write read") + param("website", "https://example.com") + } + .asyncDispatch() + .andExpect { status { isOk() } } + + + val app = RegisteredClient + .select { RegisteredClient.clientName eq "test-client" } + .single() + + assertThat(app[RegisteredClient.clientName]).isEqualTo("test-client") + assertThat(app[RegisteredClient.redirectUris]).isEqualTo("https://example.com") + assertThat(app[RegisteredClient.scopes]).isEqualTo("read,write") + } + + @Test + @WithAnonymousUser + fun apiV1AppsPostにjsonで匿名でappを作成できる() { + mockMvc + .post("/api/v1/apps") { + contentType = MediaType.APPLICATION_JSON + content = """{ + "client_name": "test-client-2", + "redirect_uris": "https://example.com", + "scopes": "write read", + "website": "https;//example.com" +}""" + } + .asyncDispatch() + .andExpect { status { isOk() } } + + val app = RegisteredClient + .select { RegisteredClient.clientName eq "test-client-2" } + .single() + + assertThat(app[RegisteredClient.clientName]).isEqualTo("test-client-2") + assertThat(app[RegisteredClient.redirectUris]).isEqualTo("https://example.com") + assertThat(app[RegisteredClient.scopes]).isEqualTo("read,write") + } +} diff --git a/src/intTest/resources/application.yml b/src/intTest/resources/application.yml index 258014ca..3de298a1 100644 --- a/src/intTest/resources/application.yml +++ b/src/intTest/resources/application.yml @@ -18,10 +18,10 @@ hideout: spring: flyway: - enabled: false + enabled: true datasource: driver-class-name: org.h2.Driver - url: "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1" + url: "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=true;TRACE_LEVEL_FILE=4;" username: "" password: data: @@ -34,8 +34,8 @@ spring: console: enabled: true - exposed: - generate-ddl: true - excluded-packages: dev.usbharu.hideout.core.infrastructure.kjobexposed +# exposed: +# generate-ddl: true +# excluded-packages: dev.usbharu.hideout.core.infrastructure.kjobexposed server: port: 8080 From a91bb89859e23beb96b18c85ebb8bbd33ddeb270 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:02:46 +0900 Subject: [PATCH 049/116] =?UTF-8?q?test:=20=E3=83=A1=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=82=A2=E3=82=A2=E3=83=83=E3=83=97=E3=83=AD=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 + src/intTest/kotlin/mastodon/MediaTest.kt | 71 ++++++++++++++++++ src/intTest/resources/media/400x400.png | Bin 0 -> 7227 bytes ...cheTikaFileTypeDeterminationServiceTest.kt | 19 +++++ .../service/media/MediaServiceImplTest.kt | 11 +++ src/test/resources/400x400.png | Bin 0 -> 7227 bytes 6 files changed, 103 insertions(+) create mode 100644 src/intTest/kotlin/mastodon/MediaTest.kt create mode 100644 src/intTest/resources/media/400x400.png create mode 100644 src/test/kotlin/dev/usbharu/hideout/core/service/media/ApatcheTikaFileTypeDeterminationServiceTest.kt create mode 100644 src/test/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImplTest.kt create mode 100644 src/test/resources/400x400.png diff --git a/build.gradle.kts b/build.gradle.kts index a17c93a4..e507ec54 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -180,6 +180,7 @@ dependencies { implementation("org.postgresql:postgresql:42.6.0") implementation("com.twelvemonkeys.imageio:imageio-webp:3.10.0") implementation("org.apache.tika:tika-core:2.9.1") + implementation("org.apache.tika:tika-parsers:2.9.1") implementation("net.coobird:thumbnailator:0.4.20") implementation("org.bytedeco:javacv-platform:1.5.9") implementation("org.flywaydb:flyway-core") @@ -208,6 +209,7 @@ dependencies { intTestImplementation("org.springframework.security:spring-security-test") intTestImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version") intTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4") + intTestImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0") } diff --git a/src/intTest/kotlin/mastodon/MediaTest.kt b/src/intTest/kotlin/mastodon/MediaTest.kt new file mode 100644 index 00000000..1a170c81 --- /dev/null +++ b/src/intTest/kotlin/mastodon/MediaTest.kt @@ -0,0 +1,71 @@ +package mastodon + +import dev.usbharu.hideout.SpringApplication +import dev.usbharu.hideout.core.service.media.MediaDataStore +import dev.usbharu.hideout.core.service.media.MediaSaveRequest +import dev.usbharu.hideout.core.service.media.SuccessSavedMedia +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.whenever +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.mock.web.MockMultipartFile +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers +import org.springframework.test.context.jdbc.Sql +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.multipart +import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder +import org.springframework.test.web.servlet.setup.MockMvcBuilders +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.context.WebApplicationContext + +@SpringBootTest(classes = [SpringApplication::class]) +@AutoConfigureMockMvc +@Transactional +@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +class MediaTest { + + @Autowired + private lateinit var context: WebApplicationContext + + private lateinit var mockMvc: MockMvc + + + @MockBean + private lateinit var mediaDataStore: MediaDataStore + + @BeforeEach + fun setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(context) + .apply(SecurityMockMvcConfigurers.springSecurity()) + .build() + } + + @Test + fun メディアをアップロードできる() = runTest { + whenever(mediaDataStore.save(any())).doReturn(SuccessSavedMedia("", "", "")) + + mockMvc + .multipart("/api/v1/media") { + + file( + MockMultipartFile( + "file", + "400x400.png", + "image/png", + String.javaClass.classLoader.getResourceAsStream("media/400x400.png") + ) + ) + with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))) + } + .asyncDispatch() + .andExpect { status { isOk() } } + } +} diff --git a/src/intTest/resources/media/400x400.png b/src/intTest/resources/media/400x400.png new file mode 100644 index 0000000000000000000000000000000000000000..0d2e71bee323fed58e3aab0a839c0c2044bba54f GIT binary patch literal 7227 zcmeHM`8$+d+~?`hLQ0!tt0W>JyTKrmEh5>MWFK3ykFD}ZVo;XSFp@3%mLX$I@z|4P zVvLDVV~H`Q84U)*d%E7=-+$nJez>o>uDQ>B&i8!JclmriC*_vK4Srr>UM?;!eiLIu zYc4MC%sme`qUtG7& z>v3_({(X>3FX_KQ|GD7*Uld5h^6XkzSd`dSai~6Lp|P3va%v630cuF=3Y(H~m`MvM zC#dD}_xtv5N8kN>@2ZL9@y7Oc?NJZKr3lQQA)~&=V2DNe1=J!(yEaN&KF{1}Y)K_) z4Y|dfPgM~{Byd)A_t+U4>Bf#=*wKX#ga;2)OsKgxjhbJhXpL3e?oqEgbL}r-p0l z=rr$79sb1BC^r&9gF!i~+%7ZqIF-@I9H zZX#w64h~kt+BQ4pG&Ly$e;e$nqZUoDt}o$OdGPAb*iqjwiNN@;0)Z_g>q{p~Y-Ib_ z*U@tF@=#=3QH-z5M4cPX6h|)H^f|@Lqp7Ves;RwOVO!PjmcY5lg6q^%U@F2)f?EPY zRDAF2QrO()W)w{SuoxkHo{rl?_2sbYzI^%e=iicFn|DgBpIa6}O`3>@S6YLDgXi(( zmL4vTn!#4JwX=b@eQs<0{q)oP{Cr}_tVwHI8*lX_a43aB(M!h=0|rJ%t?qI=$_bWF zV7h;TX?vDG9;N~hbR_JF_r(BfG$D{JLVc|**dT@C5#pkr>2diaFOx=U@v+i6?0)8% zpK!ts5-B2clD7kwWiEB`xv{af_-R6T>2#18_{g=+XNMDyQ@C;C29rU#!Mbl-j*nK+ z-mk7kVYL3!<-QaKd61AmCtVQzCgR}YQrfp*TxxxFW|41-#cDp8+FeA&lIeTZkxp0E zfiYlD8E17XgX>!yYi@L15KQG%Z```{dkt0S7UkmXY^)+&u*vCnxS$s=(+9k4F(0}E zpG!?mZER?8&mWp=tIZzt0+#l;KLjQ%E-p4=FjWxr;Gmhm-gSv%(X8bh&n5@TP^VGx z`Ij$WP87Wduc8@0uFrZ9M*dg|heL9P8B{qhr8E7DYcEVo(^&B!%Rh@jztp69cwCAlU0QIK`{0ZelS|w6ZTvm ze8>+kGm*`C!QsU9dpGqEwmG}Sb}p|_e$$JX*4FwNHwP!DEG$;XBkJ&STwvhM^^J`h zu?%olA8@#{?AFTh%5ZOYiRYOcbh49{nS_LynOP$arvQSuhk%)`b#`;pT@sei+a-em zYWVVbkNw3CyQ0oD?%3+$%8|79f&i$eK`@jNvu2A^Y!YO}k( z*SE)QPe9g|K^3|E0jF~L@25K&6_);GnKd_lXr#BtbKoT#&%pTQ@yitdA#eN?M(G0< z+wA%b2WP?N+{-~SizrMV7{=UHwJN?C*);O3R%9wp0 zoWH=?x&tSjNpz_!lW_#ySn^)r$~a85{)~dqO&nQ$W)b;s<$g{)8IrvPj2y>4VzI)h zIrjxv9j&dtSor#hj9#z6z@aS%{3{6^zG0}Iyu5rGr5r8ZtpTza;qj&DRv(;vR|u@M zxdv;7gnhTI8h<_$BSZ|C*c5B4)oPYTF@np>%X_48|GbTl#Q?drz1 z0l&xe%#3jGa`LsJhO+ZU633{wf1*F~zNWT~!J5-Qb|0pfuH1kW<)a z1%_DCa=1PE_0Hl8NIb0hH6xgASJ(Bl`(ESw_bPaJA=*a^K~PJ&`rJ58TTM&O>6l-P z$G&t*-`Lm-2Iq3lK(V{8R&8l%X+!xrc?AU#p9p?oMhW2vQ`<5MUQ5+SQDr zCnoOmu&=JIO^>zof8kh@%YeE;3UXtiXEy~>scjSAt3s$cf~h8u0=4z!|DCpK!(2)^ zLg&w{T7fyHUOx}*!)5tI^ZbZL<>%*X{|ryQc0PJ-VaU(|2Kp4lOX=yje;~s9)e-_t z&YKHiZF4GqkBRj*Jo$HMXvp{kfBc4CVC@Q`8o$R;DXxw+L8JUGuvku8K#mqu~W$f3OQJ5LorSlljeX=#y0UwlXlMw(0Qi?07t zH^VvnL`C`zosW-iB7v-Z#6O)%VlSiI+L)J^e;aT^*r6u@q*6>ZG;R0$&KZ#ju(4hO5%P{JB5&?x-QfVlMgRvV!;o zaB#t6ROK_^ygEBOCwouvCM~?3DW7-X>ATBIsncN^OG;>IX`SE%I1OKVCq8?(tHO$p zuuVre3q!~hdc=ZVt9}FarCVa-sqjqd9pa4t zl;zJOYLZHzmy5w1GQ6AoMBA8r?;IGIVfCf+YRhr!EGYl5l-|I6V~Ed2ivP)(_ zmvNK4Gb!X|anbNw{rR?J<)q?EhmT=hM2bZN9`Z{CyulAOZSBxrFn zMPxx+lS#|UZ}gWHC8a3!?GKDmz;!lb1w9j)+v;B`t-xL>0cY^Hau=f~;*`-XEsL;( zEP$dYcg7^kS9S;JfA8i~3%J-rlM$!$*E18{5_ubMlr6E%i}l%9LbWi6{>Jsb)b1ZR z)B}027_yES5K&#K@EAa^xrK#=Ps5=fz8RLUSkt?}@Y+`CrT+bWcO&)jvY0GXqn4@u zaN7-U{}p}lmu{5&o+n0XDTbYGqPzY5DXq1aK@p!kl-FZ3X*HRS-V^<$c)-bn`{I%0I~~W%20etq+HA zikAdady1-9ESBEHOe@a}OEL@N!{`>w#7Bt3EUT>97N8A?5>Msdzy=Ti0 zb4;QV_7xa;YycM{O@H3{3d4`)g%^9{bgdP_qzcYJ*Oqur<9Bspop)HA7HLqt8? z*;ksOvZNR`>tLYr*kf&Zk`V-t5>qknQV+joxVxeFeP(9n+eD1y@#g_*Rh5yrs(-h) zW2oc-8#f zD7$lCxA9E5NXdL2C&u5zHz24IL_ zzqZ=?H0y=7+Dd)m#6BtuKCvN^6L^Li?RRTBDDtPvp%^SGzo5WsM7eeY6j)ISzNBj% zI&q|sW)khA_r0MQ?12=nypTjCmj!=N6Fv6b7wwHSN+(-9DZ742HGr;M(=B(_V=BsI zu6JKznMmO{Ec{*7spYr_tRVP(>$zT?GdScpy5NA$FyD&nQad3g^I)b-1I=NE%KD4w z+cTkh#Q!2>bfPj^~*D{N$scBH!2spB0S*s;)&r6ts& zgLMAN>Z+rU0MTNS5#=l>D41qoi!kz7xtP>b6NyjI?T1^I<3D%4_F+V6|8h8jX0I%T zIkFt`%zNzb&OdOlwYB})OQdfgu)fF6$l3%VYF+Eh>IfqK3=@14Zb4 zXM3lDKoT&Q@Z|>#{%Ok3X|Asq@XeSA-bn}vu?lAndMfpB0r2d>1BkIIYQ>;vWP}@V zGx42v+o}|5n}<%_)jAeb=CJ$C=QX3>W@ge=MFEjclkRl4RBEvQJel=d+tRf3#a1gE zi((9Tk398RT3#NAI_~J~99wVqxwG?e_<^pqwKcu+htOnrosFyqzekSgMUUBwS^Y(* zk1ZTm9JyuxM@2xf!_DR#2-_tB=Wb`4OCh?{D5!Tgkq1-KCa4XZS{gRlO8zC{lVEDM zWRB_33VkF%4MD;`>2jy8zh}qJ2sLuz3mqH;K~w3evLl3#OT%zzr2$j!tap*51f;Pf zYEJo_#NT~iiSS0iN-9wSASKpI4p@Fa>K`6HuHi|5H3OcU9_x&C9d0$P?#tM|({zud z>^#@YTtq*C+OJQ0DrP4olk;f7anaEguccf;Jayemr=*2h`o_gg+tVW>BP-8D0L0YF zn4E1w$^PXz&`7LiFp1$Vnp#>Se)4eOVtE(c&3vmM>l?*Z{?~xdS7Ea}N->qnn#kfdaPao`_wU)3239#@)WGCX^iFzc0H8XoXV=&2e=aRm zgbPKx+Q}dMi(84Npe>4aP5KH`)zs8HBWnnHv#6-3kmXp>En_Bor?1yLX7v-WMngjg z*4D5X{ouiadtz^+0L1y6dj^Nas@8_jx9D!w1=z^|h~8Sd61u37uKLQXbZq44eKq7w z@S!5AM6B0}#av^zGlGHUF#qe<{dWQ(E_Ue|88sVF722;BNTJmaJgAY?p`(NIjiO>= z4(0nm+3LP$1x?ru&?yQI2zZyfmfn4D;xRo8!ep=cuy;8a)!AiMo2an^YBm9D?*MZ%stn(d!u@VS`4(Z=RLpR6e z8j_ND{9BtHudsL6P4p4Z8e35E_5m58hX8Va5yrD<5;c}8^QG#(h8zZiVUqWD2~j*& zb%goZS-ltibHgI{Iq_})M}x9NZlL7koT8rkqUhQYD+5|_6wg)r9?I61BPap?QbGqH{+Z4z!ZEdjj*B^k!10Xk^qp^UXXfy)Q zTBw_#_NAk89A;fL4&s>;3-$f=kOov2dRh%=A`Os>03QSXADq9R43v(!IYS^6OXism zfP{)dNf%fwj>0p5)P%uo&!Ou%r?{0aG}xO*L`H)17?~;#RzqI6BK^W#s;23wQA1PH zg&Nzzsmm*2kPC>7qY+Hxcw*xG1ESE=c)Jr_`BAXusYJTFVz#QnV8?JO@E-vG2+^}= zov7GR-?ZO~EBzpx1(@mfno3mt7zhMi&IyfAT zUfJdq1%<)g-S~4lJHW^E87$x2_i~`MZEtVCuqgLoibX=is2VZQLOb2G(?^o$SyVy~ z;Z=@TZoj8zaBy(-f!hZFgo~4;l*IC5Q*DM#|czK zXzb=2@ylY`m|yEOl3t89K;x^cwSWdYikV)%e3|*ZD>)Ck76OOEM44+yE7fXqDUSm8 zu4=ia($b6UO-AvSCk@c%xNTd2`u2$(L4ehI&nEOOER;dA0qU6VNMC+a{)ybQ(yvMBBn4;4o21|$#9S5gWOW@^qwqd?pN9pWVr zDQl@fqC4IjA}OA+*IuN~x3RGSLP7xul3Sb(5Cz}wZ!SK#1gb?pS;Md%s8>Lpsj)R{ zo38y0*Ey8j1tbAEIXPpcynJHTw;}H}Hs&__U1x literal 0 HcmV?d00001 diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/media/ApatcheTikaFileTypeDeterminationServiceTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/media/ApatcheTikaFileTypeDeterminationServiceTest.kt new file mode 100644 index 00000000..5b445356 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/media/ApatcheTikaFileTypeDeterminationServiceTest.kt @@ -0,0 +1,19 @@ +package dev.usbharu.hideout.core.service.media + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import kotlin.io.path.toPath + +class ApatcheTikaFileTypeDeterminationServiceTest { + @Test + fun png() { + val apatcheTikaFileTypeDeterminationService = ApatcheTikaFileTypeDeterminationService() + + val mimeType = apatcheTikaFileTypeDeterminationService.fileType( + String.javaClass.classLoader.getResource("400x400.png").toURI().toPath(), "400x400.png" + ) + + assertThat(mimeType.type).isEqualTo("image") + assertThat(mimeType.subtype).isEqualTo("png") + } +} diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImplTest.kt new file mode 100644 index 00000000..cec6e5aa --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImplTest.kt @@ -0,0 +1,11 @@ +package dev.usbharu.hideout.core.service.media + + +import org.junit.jupiter.api.Test + +class MediaServiceImplTest { + @Test + fun png画像をアップロードできる() { + + } +} diff --git a/src/test/resources/400x400.png b/src/test/resources/400x400.png new file mode 100644 index 0000000000000000000000000000000000000000..0d2e71bee323fed58e3aab0a839c0c2044bba54f GIT binary patch literal 7227 zcmeHM`8$+d+~?`hLQ0!tt0W>JyTKrmEh5>MWFK3ykFD}ZVo;XSFp@3%mLX$I@z|4P zVvLDVV~H`Q84U)*d%E7=-+$nJez>o>uDQ>B&i8!JclmriC*_vK4Srr>UM?;!eiLIu zYc4MC%sme`qUtG7& z>v3_({(X>3FX_KQ|GD7*Uld5h^6XkzSd`dSai~6Lp|P3va%v630cuF=3Y(H~m`MvM zC#dD}_xtv5N8kN>@2ZL9@y7Oc?NJZKr3lQQA)~&=V2DNe1=J!(yEaN&KF{1}Y)K_) z4Y|dfPgM~{Byd)A_t+U4>Bf#=*wKX#ga;2)OsKgxjhbJhXpL3e?oqEgbL}r-p0l z=rr$79sb1BC^r&9gF!i~+%7ZqIF-@I9H zZX#w64h~kt+BQ4pG&Ly$e;e$nqZUoDt}o$OdGPAb*iqjwiNN@;0)Z_g>q{p~Y-Ib_ z*U@tF@=#=3QH-z5M4cPX6h|)H^f|@Lqp7Ves;RwOVO!PjmcY5lg6q^%U@F2)f?EPY zRDAF2QrO()W)w{SuoxkHo{rl?_2sbYzI^%e=iicFn|DgBpIa6}O`3>@S6YLDgXi(( zmL4vTn!#4JwX=b@eQs<0{q)oP{Cr}_tVwHI8*lX_a43aB(M!h=0|rJ%t?qI=$_bWF zV7h;TX?vDG9;N~hbR_JF_r(BfG$D{JLVc|**dT@C5#pkr>2diaFOx=U@v+i6?0)8% zpK!ts5-B2clD7kwWiEB`xv{af_-R6T>2#18_{g=+XNMDyQ@C;C29rU#!Mbl-j*nK+ z-mk7kVYL3!<-QaKd61AmCtVQzCgR}YQrfp*TxxxFW|41-#cDp8+FeA&lIeTZkxp0E zfiYlD8E17XgX>!yYi@L15KQG%Z```{dkt0S7UkmXY^)+&u*vCnxS$s=(+9k4F(0}E zpG!?mZER?8&mWp=tIZzt0+#l;KLjQ%E-p4=FjWxr;Gmhm-gSv%(X8bh&n5@TP^VGx z`Ij$WP87Wduc8@0uFrZ9M*dg|heL9P8B{qhr8E7DYcEVo(^&B!%Rh@jztp69cwCAlU0QIK`{0ZelS|w6ZTvm ze8>+kGm*`C!QsU9dpGqEwmG}Sb}p|_e$$JX*4FwNHwP!DEG$;XBkJ&STwvhM^^J`h zu?%olA8@#{?AFTh%5ZOYiRYOcbh49{nS_LynOP$arvQSuhk%)`b#`;pT@sei+a-em zYWVVbkNw3CyQ0oD?%3+$%8|79f&i$eK`@jNvu2A^Y!YO}k( z*SE)QPe9g|K^3|E0jF~L@25K&6_);GnKd_lXr#BtbKoT#&%pTQ@yitdA#eN?M(G0< z+wA%b2WP?N+{-~SizrMV7{=UHwJN?C*);O3R%9wp0 zoWH=?x&tSjNpz_!lW_#ySn^)r$~a85{)~dqO&nQ$W)b;s<$g{)8IrvPj2y>4VzI)h zIrjxv9j&dtSor#hj9#z6z@aS%{3{6^zG0}Iyu5rGr5r8ZtpTza;qj&DRv(;vR|u@M zxdv;7gnhTI8h<_$BSZ|C*c5B4)oPYTF@np>%X_48|GbTl#Q?drz1 z0l&xe%#3jGa`LsJhO+ZU633{wf1*F~zNWT~!J5-Qb|0pfuH1kW<)a z1%_DCa=1PE_0Hl8NIb0hH6xgASJ(Bl`(ESw_bPaJA=*a^K~PJ&`rJ58TTM&O>6l-P z$G&t*-`Lm-2Iq3lK(V{8R&8l%X+!xrc?AU#p9p?oMhW2vQ`<5MUQ5+SQDr zCnoOmu&=JIO^>zof8kh@%YeE;3UXtiXEy~>scjSAt3s$cf~h8u0=4z!|DCpK!(2)^ zLg&w{T7fyHUOx}*!)5tI^ZbZL<>%*X{|ryQc0PJ-VaU(|2Kp4lOX=yje;~s9)e-_t z&YKHiZF4GqkBRj*Jo$HMXvp{kfBc4CVC@Q`8o$R;DXxw+L8JUGuvku8K#mqu~W$f3OQJ5LorSlljeX=#y0UwlXlMw(0Qi?07t zH^VvnL`C`zosW-iB7v-Z#6O)%VlSiI+L)J^e;aT^*r6u@q*6>ZG;R0$&KZ#ju(4hO5%P{JB5&?x-QfVlMgRvV!;o zaB#t6ROK_^ygEBOCwouvCM~?3DW7-X>ATBIsncN^OG;>IX`SE%I1OKVCq8?(tHO$p zuuVre3q!~hdc=ZVt9}FarCVa-sqjqd9pa4t zl;zJOYLZHzmy5w1GQ6AoMBA8r?;IGIVfCf+YRhr!EGYl5l-|I6V~Ed2ivP)(_ zmvNK4Gb!X|anbNw{rR?J<)q?EhmT=hM2bZN9`Z{CyulAOZSBxrFn zMPxx+lS#|UZ}gWHC8a3!?GKDmz;!lb1w9j)+v;B`t-xL>0cY^Hau=f~;*`-XEsL;( zEP$dYcg7^kS9S;JfA8i~3%J-rlM$!$*E18{5_ubMlr6E%i}l%9LbWi6{>Jsb)b1ZR z)B}027_yES5K&#K@EAa^xrK#=Ps5=fz8RLUSkt?}@Y+`CrT+bWcO&)jvY0GXqn4@u zaN7-U{}p}lmu{5&o+n0XDTbYGqPzY5DXq1aK@p!kl-FZ3X*HRS-V^<$c)-bn`{I%0I~~W%20etq+HA zikAdady1-9ESBEHOe@a}OEL@N!{`>w#7Bt3EUT>97N8A?5>Msdzy=Ti0 zb4;QV_7xa;YycM{O@H3{3d4`)g%^9{bgdP_qzcYJ*Oqur<9Bspop)HA7HLqt8? z*;ksOvZNR`>tLYr*kf&Zk`V-t5>qknQV+joxVxeFeP(9n+eD1y@#g_*Rh5yrs(-h) zW2oc-8#f zD7$lCxA9E5NXdL2C&u5zHz24IL_ zzqZ=?H0y=7+Dd)m#6BtuKCvN^6L^Li?RRTBDDtPvp%^SGzo5WsM7eeY6j)ISzNBj% zI&q|sW)khA_r0MQ?12=nypTjCmj!=N6Fv6b7wwHSN+(-9DZ742HGr;M(=B(_V=BsI zu6JKznMmO{Ec{*7spYr_tRVP(>$zT?GdScpy5NA$FyD&nQad3g^I)b-1I=NE%KD4w z+cTkh#Q!2>bfPj^~*D{N$scBH!2spB0S*s;)&r6ts& zgLMAN>Z+rU0MTNS5#=l>D41qoi!kz7xtP>b6NyjI?T1^I<3D%4_F+V6|8h8jX0I%T zIkFt`%zNzb&OdOlwYB})OQdfgu)fF6$l3%VYF+Eh>IfqK3=@14Zb4 zXM3lDKoT&Q@Z|>#{%Ok3X|Asq@XeSA-bn}vu?lAndMfpB0r2d>1BkIIYQ>;vWP}@V zGx42v+o}|5n}<%_)jAeb=CJ$C=QX3>W@ge=MFEjclkRl4RBEvQJel=d+tRf3#a1gE zi((9Tk398RT3#NAI_~J~99wVqxwG?e_<^pqwKcu+htOnrosFyqzekSgMUUBwS^Y(* zk1ZTm9JyuxM@2xf!_DR#2-_tB=Wb`4OCh?{D5!Tgkq1-KCa4XZS{gRlO8zC{lVEDM zWRB_33VkF%4MD;`>2jy8zh}qJ2sLuz3mqH;K~w3evLl3#OT%zzr2$j!tap*51f;Pf zYEJo_#NT~iiSS0iN-9wSASKpI4p@Fa>K`6HuHi|5H3OcU9_x&C9d0$P?#tM|({zud z>^#@YTtq*C+OJQ0DrP4olk;f7anaEguccf;Jayemr=*2h`o_gg+tVW>BP-8D0L0YF zn4E1w$^PXz&`7LiFp1$Vnp#>Se)4eOVtE(c&3vmM>l?*Z{?~xdS7Ea}N->qnn#kfdaPao`_wU)3239#@)WGCX^iFzc0H8XoXV=&2e=aRm zgbPKx+Q}dMi(84Npe>4aP5KH`)zs8HBWnnHv#6-3kmXp>En_Bor?1yLX7v-WMngjg z*4D5X{ouiadtz^+0L1y6dj^Nas@8_jx9D!w1=z^|h~8Sd61u37uKLQXbZq44eKq7w z@S!5AM6B0}#av^zGlGHUF#qe<{dWQ(E_Ue|88sVF722;BNTJmaJgAY?p`(NIjiO>= z4(0nm+3LP$1x?ru&?yQI2zZyfmfn4D;xRo8!ep=cuy;8a)!AiMo2an^YBm9D?*MZ%stn(d!u@VS`4(Z=RLpR6e z8j_ND{9BtHudsL6P4p4Z8e35E_5m58hX8Va5yrD<5;c}8^QG#(h8zZiVUqWD2~j*& zb%goZS-ltibHgI{Iq_})M}x9NZlL7koT8rkqUhQYD+5|_6wg)r9?I61BPap?QbGqH{+Z4z!ZEdjj*B^k!10Xk^qp^UXXfy)Q zTBw_#_NAk89A;fL4&s>;3-$f=kOov2dRh%=A`Os>03QSXADq9R43v(!IYS^6OXism zfP{)dNf%fwj>0p5)P%uo&!Ou%r?{0aG}xO*L`H)17?~;#RzqI6BK^W#s;23wQA1PH zg&Nzzsmm*2kPC>7qY+Hxcw*xG1ESE=c)Jr_`BAXusYJTFVz#QnV8?JO@E-vG2+^}= zov7GR-?ZO~EBzpx1(@mfno3mt7zhMi&IyfAT zUfJdq1%<)g-S~4lJHW^E87$x2_i~`MZEtVCuqgLoibX=is2VZQLOb2G(?^o$SyVy~ z;Z=@TZoj8zaBy(-f!hZFgo~4;l*IC5Q*DM#|czK zXzb=2@ylY`m|yEOl3t89K;x^cwSWdYikV)%e3|*ZD>)Ck76OOEM44+yE7fXqDUSm8 zu4=ia($b6UO-AvSCk@c%xNTd2`u2$(L4ehI&nEOOER;dA0qU6VNMC+a{)ybQ(yvMBBn4;4o21|$#9S5gWOW@^qwqd?pN9pWVr zDQl@fqC4IjA}OA+*IuN~x3RGSLP7xul3Sb(5Cz}wZ!SK#1gb?pS;Md%s8>Lpsj)R{ zo38y0*Ey8j1tbAEIXPpcynJHTw;}H}Hs&__U1x literal 0 HcmV?d00001 From 2c63cad80e6aa15c8103ce256cf7bf344cc09663 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:03:21 +0900 Subject: [PATCH 050/116] =?UTF-8?q?fix:=20png=E5=BD=A2=E5=BC=8F=E3=81=AE?= =?UTF-8?q?=E7=94=BB=E5=83=8F=E3=81=AE=E4=B8=80=E6=99=82=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=81=AE=E4=BD=9C=E6=88=90=E3=81=AB=E5=A4=B1?= =?UTF-8?q?=E6=95=97=E3=81=99=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../image/ImageMediaProcessService.kt | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessService.kt index d61d32ef..c713a63e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessService.kt @@ -13,6 +13,8 @@ import net.coobird.thumbnailator.Thumbnails import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Service +import java.awt.Color +import java.awt.image.BufferedImage import java.nio.file.Files import java.nio.file.Path import java.util.* @@ -57,7 +59,14 @@ class ImageMediaProcessService(private val imageMediaProcessorConfiguration: Ima filePath: Path, thumbnails: Path? ): ProcessedMediaPath = withContext(Dispatchers.IO + MDCContext()) { - val bufferedImage = ImageIO.read(filePath.inputStream()) + val read = ImageIO.read(filePath.inputStream()) + + val bufferedImage = BufferedImage(read.width, read.height, BufferedImage.TYPE_INT_RGB) + + val graphics = bufferedImage.createGraphics() + + graphics.drawImage(read, 0, 0, Color.BLACK, null) + val tempFileName = UUID.randomUUID().toString() val tempFile = Files.createTempFile(tempFileName, "tmp") @@ -67,9 +76,15 @@ class ImageMediaProcessService(private val imageMediaProcessorConfiguration: Ima tempThumbnailFile.outputStream().use { val write = ImageIO.write( if (thumbnails != null) { - Thumbnails.of(thumbnails.toFile()).size(width, height).asBufferedImage() + Thumbnails.of(thumbnails.toFile()) + .size(width, height) + .imageType(BufferedImage.TYPE_INT_RGB) + .asBufferedImage() } else { - Thumbnails.of(bufferedImage).size(width, height).asBufferedImage() + Thumbnails.of(bufferedImage) + .size(width, height) + .imageType(BufferedImage.TYPE_INT_RGB) + .asBufferedImage() }, convertType, it From 072767b56a1ba85a834cf41d9a6eb526327544a4 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:17:52 +0900 Subject: [PATCH 051/116] =?UTF-8?q?test:=20=E3=83=A1=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=82=A2=E3=82=A2=E3=83=83=E3=83=97=E3=83=AD=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=81=AE=E3=82=B9=E3=82=B3=E3=83=BC=E3=83=97=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/intTest/kotlin/mastodon/MediaTest.kt | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/intTest/kotlin/mastodon/MediaTest.kt b/src/intTest/kotlin/mastodon/MediaTest.kt index 1a170c81..d3e54e88 100644 --- a/src/intTest/kotlin/mastodon/MediaTest.kt +++ b/src/intTest/kotlin/mastodon/MediaTest.kt @@ -68,4 +68,46 @@ class MediaTest { .asyncDispatch() .andExpect { status { isOk() } } } + + @Test + fun write_mediaスコープでメディアをアップロードできる() = runTest { + whenever(mediaDataStore.save(any())).doReturn(SuccessSavedMedia("", "", "")) + + mockMvc + .multipart("/api/v1/media") { + + file( + MockMultipartFile( + "file", + "400x400.png", + "image/png", + String.javaClass.classLoader.getResourceAsStream("media/400x400.png") + ) + ) + with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:media"))) + } + .asyncDispatch() + .andExpect { status { isOk() } } + } + + @Test + fun 権限がないと403() = runTest { + whenever(mediaDataStore.save(any())).doReturn(SuccessSavedMedia("", "", "")) + + mockMvc + .multipart("/api/v1/media") { + + file( + MockMultipartFile( + "file", + "400x400.png", + "image/png", + String.javaClass.classLoader.getResourceAsStream("media/400x400.png") + ) + ) + with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))) + } + .andExpect { status { isForbidden() } } + } + } From db6f9d5eb1428ddd2705b3e1b8987e2cadcc3d99 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:18:12 +0900 Subject: [PATCH 052/116] =?UTF-8?q?fix:=20=E3=83=A1=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=82=A2=E3=82=A2=E3=83=83=E3=83=97=E3=83=AD=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=81=AE=E3=82=B9=E3=82=B3=E3=83=BC=E3=83=97=E3=81=AE=E8=A8=AD?= =?UTF-8?q?=E5=AE=9A=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/usbharu/hideout/application/config/SecurityConfig.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index 089a545a..7d4b2d8e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -190,6 +190,8 @@ class SecurityConfig { it.requestMatchers(builder.pattern("/change-password")).authenticated() it.requestMatchers(builder.pattern("/api/v1/accounts/verify_credentials")) .hasAnyAuthority("SCOPE_read", "SCOPE_read:accounts") + it.requestMatchers(builder.pattern(HttpMethod.POST, "/api/v1/media")) + .hasAnyAuthority("SCOPE_write", "SCOPE_write:media") it.anyRequest().permitAll() } http.oauth2ResourceServer { From 3f4d24ba1c092ffb39edb22c481c20986034e3f1 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:33:53 +0900 Subject: [PATCH 053/116] =?UTF-8?q?refactor:=20MediaTest.kt=E3=82=92?= =?UTF-8?q?=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/intTest/kotlin/mastodon/{ => media}/MediaTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/intTest/kotlin/mastodon/{ => media}/MediaTest.kt (99%) diff --git a/src/intTest/kotlin/mastodon/MediaTest.kt b/src/intTest/kotlin/mastodon/media/MediaTest.kt similarity index 99% rename from src/intTest/kotlin/mastodon/MediaTest.kt rename to src/intTest/kotlin/mastodon/media/MediaTest.kt index d3e54e88..898462df 100644 --- a/src/intTest/kotlin/mastodon/MediaTest.kt +++ b/src/intTest/kotlin/mastodon/media/MediaTest.kt @@ -1,4 +1,4 @@ -package mastodon +package mastodon.media import dev.usbharu.hideout.SpringApplication import dev.usbharu.hideout.core.service.media.MediaDataStore From 032342262cd69aaa90c34ecf4cf5003ea9fc6c1f Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:23:59 +0900 Subject: [PATCH 054/116] =?UTF-8?q?test:=20Status=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/mastodon/status/StatusTest.kt | 145 ++++++++++++++++++ src/intTest/resources/logback.xml | 1 + src/intTest/resources/sql/test-post.sql | 3 + 3 files changed, 149 insertions(+) create mode 100644 src/intTest/kotlin/mastodon/status/StatusTest.kt create mode 100644 src/intTest/resources/sql/test-post.sql diff --git a/src/intTest/kotlin/mastodon/status/StatusTest.kt b/src/intTest/kotlin/mastodon/status/StatusTest.kt new file mode 100644 index 00000000..2272895a --- /dev/null +++ b/src/intTest/kotlin/mastodon/status/StatusTest.kt @@ -0,0 +1,145 @@ +package mastodon.status + +import dev.usbharu.hideout.SpringApplication +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.MediaType +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.security.test.context.support.WithAnonymousUser +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers +import org.springframework.test.context.jdbc.Sql +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.post +import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder +import org.springframework.test.web.servlet.setup.MockMvcBuilders +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.context.WebApplicationContext + +@SpringBootTest(classes = [SpringApplication::class]) +@AutoConfigureMockMvc +@Transactional +@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +class StatusTest { + + @Autowired + private lateinit var context: WebApplicationContext + + private lateinit var mockMvc: MockMvc + + + @BeforeEach + fun setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(context) + .apply(SecurityMockMvcConfigurers.springSecurity()) + .build() + } + + @Test + fun 投稿できる() { + mockMvc + .post("/api/v1/statuses") { + contentType = MediaType.APPLICATION_JSON + content = """{"status":"hello"}""" + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) + ) + } + .asyncDispatch() + .andExpect { status { isOk() } } + } + + @Test + fun write_statusesスコープで投稿できる() { + mockMvc + .post("/api/v1/statuses") { + contentType = MediaType.APPLICATION_JSON + content = """{"status":"hello"}""" + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:statuses")) + ) + } + .asyncDispatch() + .andExpect { status { isOk() } } + } + + @Test + fun 権限がないと403() { + mockMvc + .post("/api/v1/statuses") { + contentType = MediaType.APPLICATION_JSON + content = """{"status":"hello"}""" + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .andExpect { status { isForbidden() } } + } + + @Test + @WithAnonymousUser + fun 匿名だと401() { + mockMvc + .post("/api/v1/statuses") { + contentType = MediaType.APPLICATION_JSON + content = """{"status":"hello"}""" + with(csrf()) + } + .andExpect { status { isUnauthorized() } } + } + + @Test + @WithAnonymousUser + fun 匿名の場合通常はcsrfが無いので403() { + mockMvc + .post("/api/v1/statuses") { + contentType = MediaType.APPLICATION_JSON + content = """{"status":"hello"}""" + } + .andExpect { status { isForbidden() } } + } + + @Test + fun formでも投稿できる() { + mockMvc + .post("/api/v1/statuses") { + contentType = MediaType.APPLICATION_FORM_URLENCODED + param("status", "hello") + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:statuses")) + ) + } + .asyncDispatch() + .andExpect { status { isOk() } } + } + + @Test + @Sql("/sql/test-post.sql") + fun in_reply_to_idを指定したら返信として処理される() { + mockMvc + .post("/api/v1/statuses") { + contentType = MediaType.APPLICATION_JSON + //language=JSON + content = """{ + "status": "hello", + "in_reply_to_id": "1" +}""" + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) + ) + } + .asyncDispatch() + .andDo { print() } + .andExpect { status { isOk() } } + .andExpect { jsonPath("\$.in_reply_to_id") { value("1") } } + } +} diff --git a/src/intTest/resources/logback.xml b/src/intTest/resources/logback.xml index 54cfd39a..a8bb21c4 100644 --- a/src/intTest/resources/logback.xml +++ b/src/intTest/resources/logback.xml @@ -7,4 +7,5 @@ + diff --git a/src/intTest/resources/sql/test-post.sql b/src/intTest/resources/sql/test-post.sql new file mode 100644 index 00000000..01bcb2dd --- /dev/null +++ b/src/intTest/resources/sql/test-post.sql @@ -0,0 +1,3 @@ +insert into posts (id, user_id, overview, text, created_at, visibility, url, repost_id, reply_id, sensitive, ap_id) +VALUES (1, 1, null, 'hello', 1234455, 0, 'https://localhost/users/1/posts/1', null, null, false, + 'https://users/1/posts/1'); From 0b63f95c17e5930e50affc9484c0720b67883898 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:24:30 +0900 Subject: [PATCH 055/116] =?UTF-8?q?fix:=20=E8=BF=94=E4=BF=A1=E3=81=8C?= =?UTF-8?q?=E7=84=A1=E8=A6=96=E3=81=95=E3=82=8C=E3=81=A6=E3=81=84=E3=81=9F?= =?UTF-8?q?=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usbharu/hideout/core/service/post/PostServiceImpl.kt | 4 +++- .../mastodon/service/status/StatusesApiService.kt | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt index 027b3274..2e2c6c0c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt @@ -65,7 +65,9 @@ class PostServiceImpl( createdAt = Instant.now().toEpochMilli(), visibility = post.visibility, url = "${user.url}/posts/$id", - mediaIds = post.mediaIds + mediaIds = post.mediaIds, + replyId = post.repolyId, + repostId = post.repostId, ) return internalCreate(createPost, isLocal) } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt index 10b1a0c5..198681ce 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt @@ -13,6 +13,7 @@ import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusesRequest import dev.usbharu.hideout.mastodon.interfaces.api.status.toPostVisibility import dev.usbharu.hideout.mastodon.interfaces.api.status.toStatusVisibility import dev.usbharu.hideout.mastodon.service.account.AccountService +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import java.time.Instant @@ -38,12 +39,14 @@ class StatsesApiServiceImpl( statusesRequest: StatusesRequest, userId: Long ): Status = transaction.transaction { + logger.debug("START create post by mastodon api. {}", statusesRequest) + val post = postService.createLocal( PostCreateDto( text = statusesRequest.status.orEmpty(), overview = statusesRequest.spoiler_text, visibility = statusesRequest.visibility.toPostVisibility(), - repolyId = statusesRequest.in_reply_to_id?.toLongOrNull(), + repolyId = statusesRequest.in_reply_to_id?.toLong(), userId = userId, mediaIds = statusesRequest.media_ids.map { it.toLong() } ) @@ -91,4 +94,8 @@ class StatsesApiServiceImpl( editedAt = null, ) } + + companion object { + private val logger = LoggerFactory.getLogger(StatusesApiService::class.java) + } } From 8746258104da0147a76009e6de699171022230eb Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:24:54 +0900 Subject: [PATCH 056/116] =?UTF-8?q?fix:=20=E6=8A=95=E7=A8=BF=E3=81=AE?= =?UTF-8?q?=E3=82=B9=E3=82=B3=E3=83=BC=E3=83=97=E3=81=AE=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usbharu/hideout/application/config/SecurityConfig.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index 7d4b2d8e..b29a8e94 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -182,7 +182,8 @@ class SecurityConfig { builder.pattern("/api/v1/instance/**"), builder.pattern("/.well-known/**"), builder.pattern("/error"), - builder.pattern("/nodeinfo/2.0") + builder.pattern("/nodeinfo/2.0"), + builder.pattern("/api/v1/accounts") ).permitAll() it.requestMatchers( builder.pattern("/auth/**") @@ -192,7 +193,9 @@ class SecurityConfig { .hasAnyAuthority("SCOPE_read", "SCOPE_read:accounts") it.requestMatchers(builder.pattern(HttpMethod.POST, "/api/v1/media")) .hasAnyAuthority("SCOPE_write", "SCOPE_write:media") - it.anyRequest().permitAll() + it.requestMatchers(builder.pattern(HttpMethod.POST, "/api/v1/statuses")) + .hasAnyAuthority("SCOPE_write", "SCOPE_write:statuses") + it.anyRequest().authenticated() } http.oauth2ResourceServer { it.jwt(Customizer.withDefaults()) From 18e675367cf34c6a9beab632882c97f9648f40ce Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:53:01 +0900 Subject: [PATCH 057/116] style: fix lint --- .../usbharu/hideout/activitypub/domain/model/Delete.kt | 4 ++-- .../dev/usbharu/hideout/activitypub/domain/model/Undo.kt | 9 ++++----- .../usbharu/hideout/application/config/SecurityConfig.kt | 4 ---- .../hideout/core/domain/model/instance/Nodeinfo2_0.kt | 6 +++--- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt index f2142722..6f691492 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt @@ -43,8 +43,8 @@ open class Delete : Object, HasId, HasActor { override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (apObject?.hashCode() ?: 0) - result = 31 * result + (published?.hashCode() ?: 0) + result = 31 * result + apObject.hashCode() + result = 31 * result + published.hashCode() result = 31 * result + actor.hashCode() result = 31 * result + id.hashCode() return result diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt index d02ccc4d..01dbc17c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt @@ -30,14 +30,13 @@ open class Undo( override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (`object`?.hashCode() ?: 0) - result = 31 * result + (published?.hashCode() ?: 0) + result = 31 * result + `object`.hashCode() + result = 31 * result + published.hashCode() result = 31 * result + actor.hashCode() result = 31 * result + id.hashCode() return result } - override fun toString(): String { - return "Undo(`object`=$`object`, published=$published, actor='$actor', id='$id') ${super.toString()}" - } + override fun toString(): String = + "Undo(`object`=$`object`, published=$published, actor='$actor', id='$id') ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index 499e68dd..9801c6da 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -6,7 +6,6 @@ import com.nimbusds.jose.jwk.RSAKey import com.nimbusds.jose.jwk.source.ImmutableJWKSet import com.nimbusds.jose.jwk.source.JWKSource import com.nimbusds.jose.proc.SecurityContext -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureFilter import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService @@ -116,9 +115,6 @@ class SecurityConfig { @Bean fun getHttpSignatureFilter( authenticationManager: AuthenticationManager, - transaction: Transaction, - apUserService: APUserService, - userQueryService: UserQueryService ): HttpSignatureFilter { val httpSignatureFilter = HttpSignatureFilter(DefaultSignatureHeaderParser()) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt index 98247150..97478228 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt @@ -3,17 +3,17 @@ package dev.usbharu.hideout.core.domain.model.instance @Suppress("ClassNaming") -class Nodeinfo2_0() { +class Nodeinfo2_0 { var metadata: Metadata? = null var software: Software? = null } -class Metadata() { +class Metadata { var nodeName: String? = null var nodeDescription: String? = null } -class Software() { +class Software { var name: String? = null var version: String? = null } From dd8a08b84d9a49a31ec6e4b5af0539a4aad79b41 Mon Sep 17 00:00:00 2001 From: usbharu Date: Wed, 29 Nov 2023 16:55:29 +0900 Subject: [PATCH 058/116] Update integration-test.yml --- .github/workflows/integration-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 1788e609..ca866978 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -64,4 +64,4 @@ jobs: uses: mikepenz/action-junit-report@v2 if: always() with: - report_paths: '**/build/test-results/test/TEST-*.xml' + report_paths: '**/build/test-results/integrationTest/TEST-*.xml' From 2d5f547f136140a574fc85b816719d9286da79b9 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 29 Nov 2023 18:25:02 +0900 Subject: [PATCH 059/116] chore: build logging --- build.gradle.kts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index e507ec54..dfa827dd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,5 @@ +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestLogEvent import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.openapitools.generator.gradle.plugin.tasks.GenerateTask import kotlin.math.max @@ -68,6 +70,21 @@ tasks.withType { "--add-opens", "java.base/java.lang=ALL-UNNAMED" ).toMutableList() } + testLogging { + events( + TestLogEvent.FAILED, + TestLogEvent.SKIPPED, + TestLogEvent.PASSED, + TestLogEvent.STANDARD_OUT, + TestLogEvent.STANDARD_ERROR, + TestLogEvent.STARTED + ) + exceptionFormat = TestExceptionFormat.FULL + showCauses = true + showExceptions = true + showStackTraces = true + setShowStandardStreams(true) + } } tasks.withType>().configureEach { From 2175653c54a59505e7c51b896343561fc594f42b Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 00:50:18 +0900 Subject: [PATCH 060/116] =?UTF-8?q?test:=20=E5=85=A8=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E5=BE=8C=E3=81=ABDB=E3=82=92Drop=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 5 ++--- src/intTest/kotlin/activitypub/inbox/InboxTest.kt | 11 +++++++++++ src/intTest/kotlin/activitypub/note/NoteTest.kt | 11 +++++++++++ .../kotlin/activitypub/webfinger/WebFingerTest.kt | 12 ++++++++++++ .../kotlin/mastodon/account/AccountApiTest.kt | 12 +++++++++++- src/intTest/kotlin/mastodon/apps/AppTest.kt | 11 +++++++++++ src/intTest/kotlin/mastodon/media/MediaTest.kt | 11 +++++++++++ src/intTest/kotlin/mastodon/status/StatusTest.kt | 11 +++++++++++ src/intTest/resources/application.yml | 1 + 9 files changed, 81 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index dfa827dd..a5d52f43 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,6 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.openapitools.generator.gradle.plugin.tasks.GenerateTask -import kotlin.math.max val ktor_version: String by project val kotlin_version: String by project @@ -63,8 +62,8 @@ tasks.check { dependsOn(integrationTest) } tasks.withType { useJUnitPlatform() val cpus = Runtime.getRuntime().availableProcessors() - maxParallelForks = max(1, cpus - 1) - setForkEvery(4) +// maxParallelForks = max(1, cpus - 1) +// setForkEvery(4) doFirst { jvmArgs = arrayOf( "--add-opens", "java.base/java.lang=ALL-UNNAMED" diff --git a/src/intTest/kotlin/activitypub/inbox/InboxTest.kt b/src/intTest/kotlin/activitypub/inbox/InboxTest.kt index 080639b4..a1d2c64f 100644 --- a/src/intTest/kotlin/activitypub/inbox/InboxTest.kt +++ b/src/intTest/kotlin/activitypub/inbox/InboxTest.kt @@ -1,6 +1,8 @@ package activitypub.inbox import dev.usbharu.hideout.SpringApplication +import org.flywaydb.core.Flyway +import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired @@ -92,4 +94,13 @@ class InboxTest { @Bean fun testTransaction() = TestTransaction } + + companion object { + @JvmStatic + @AfterAll + fun dropDatabase(@Autowired flyway: Flyway) { + flyway.clean() + flyway.migrate() + } + } } diff --git a/src/intTest/kotlin/activitypub/note/NoteTest.kt b/src/intTest/kotlin/activitypub/note/NoteTest.kt index a559ca66..d2067aa7 100644 --- a/src/intTest/kotlin/activitypub/note/NoteTest.kt +++ b/src/intTest/kotlin/activitypub/note/NoteTest.kt @@ -1,6 +1,8 @@ package activitypub.note import dev.usbharu.hideout.SpringApplication +import org.flywaydb.core.Flyway +import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired @@ -178,4 +180,13 @@ class NoteTest { .andExpect { jsonPath("\$.attachment[1].type") { value("Document") } } .andExpect { jsonPath("\$.attachment[1].url") { value("https://example.com/media/test-media2.png") } } } + + companion object { + @JvmStatic + @AfterAll + fun dropDatabase(@Autowired flyway: Flyway) { + flyway.clean() + flyway.migrate() + } + } } diff --git a/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt b/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt index d0faaa7f..abee25d6 100644 --- a/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt +++ b/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt @@ -2,6 +2,8 @@ package activitypub.webfinger import dev.usbharu.hideout.SpringApplication import dev.usbharu.hideout.application.external.Transaction +import org.flywaydb.core.Flyway +import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc @@ -24,6 +26,7 @@ class WebFingerTest { @Test @Sql("/sql/test-user.sql") + fun `webfinger 存在するユーザーを取得`() { mockMvc .get("/.well-known/webfinger?resource=acct:test-user@example.com") @@ -80,4 +83,13 @@ class WebFingerTest { @Bean fun testTransaction(): Transaction = TestTransaction } + + companion object { + @JvmStatic + @AfterAll + fun dropDatabase(@Autowired flyway: Flyway) { + flyway.clean() + flyway.migrate() + } + } } diff --git a/src/intTest/kotlin/mastodon/account/AccountApiTest.kt b/src/intTest/kotlin/mastodon/account/AccountApiTest.kt index 88168a51..b08e2cc5 100644 --- a/src/intTest/kotlin/mastodon/account/AccountApiTest.kt +++ b/src/intTest/kotlin/mastodon/account/AccountApiTest.kt @@ -3,6 +3,8 @@ package mastodon.account import dev.usbharu.hideout.SpringApplication import dev.usbharu.hideout.core.infrastructure.exposedquery.UserQueryServiceImpl import kotlinx.coroutines.test.runTest +import org.flywaydb.core.Flyway +import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired @@ -37,7 +39,6 @@ class AccountApiTest { private lateinit var mockMvc: MockMvc - @BeforeEach fun setUp() { mockMvc = MockMvcBuilders.webAppContextSetup(context) @@ -133,4 +134,13 @@ class AccountApiTest { } .andExpect { status { isBadRequest() } } } + + companion object { + @JvmStatic + @AfterAll + fun dropDatabase(@Autowired flyway: Flyway) { + flyway.clean() + flyway.migrate() + } + } } diff --git a/src/intTest/kotlin/mastodon/apps/AppTest.kt b/src/intTest/kotlin/mastodon/apps/AppTest.kt index f835d3be..026a9163 100644 --- a/src/intTest/kotlin/mastodon/apps/AppTest.kt +++ b/src/intTest/kotlin/mastodon/apps/AppTest.kt @@ -3,7 +3,9 @@ package mastodon.apps import dev.usbharu.hideout.SpringApplication import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.RegisteredClient import org.assertj.core.api.Assertions.assertThat +import org.flywaydb.core.Flyway import org.jetbrains.exposed.sql.select +import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired @@ -85,4 +87,13 @@ class AppTest { assertThat(app[RegisteredClient.redirectUris]).isEqualTo("https://example.com") assertThat(app[RegisteredClient.scopes]).isEqualTo("read,write") } + + companion object { + @JvmStatic + @AfterAll + fun dropDatabase(@Autowired flyway: Flyway) { + flyway.clean() + flyway.migrate() + } + } } diff --git a/src/intTest/kotlin/mastodon/media/MediaTest.kt b/src/intTest/kotlin/mastodon/media/MediaTest.kt index 898462df..4c9ee710 100644 --- a/src/intTest/kotlin/mastodon/media/MediaTest.kt +++ b/src/intTest/kotlin/mastodon/media/MediaTest.kt @@ -5,6 +5,8 @@ import dev.usbharu.hideout.core.service.media.MediaDataStore import dev.usbharu.hideout.core.service.media.MediaSaveRequest import dev.usbharu.hideout.core.service.media.SuccessSavedMedia import kotlinx.coroutines.test.runTest +import org.flywaydb.core.Flyway +import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.kotlin.any @@ -110,4 +112,13 @@ class MediaTest { .andExpect { status { isForbidden() } } } + companion object { + @JvmStatic + @AfterAll + fun dropDatabase(@Autowired flyway: Flyway) { + flyway.clean() + flyway.migrate() + } + } + } diff --git a/src/intTest/kotlin/mastodon/status/StatusTest.kt b/src/intTest/kotlin/mastodon/status/StatusTest.kt index 2272895a..54b61c9d 100644 --- a/src/intTest/kotlin/mastodon/status/StatusTest.kt +++ b/src/intTest/kotlin/mastodon/status/StatusTest.kt @@ -1,6 +1,8 @@ package mastodon.status import dev.usbharu.hideout.SpringApplication +import org.flywaydb.core.Flyway +import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired @@ -142,4 +144,13 @@ class StatusTest { .andExpect { status { isOk() } } .andExpect { jsonPath("\$.in_reply_to_id") { value("1") } } } + + companion object { + @JvmStatic + @AfterAll + fun dropDatabase(@Autowired flyway: Flyway) { + flyway.clean() + flyway.migrate() + } + } } diff --git a/src/intTest/resources/application.yml b/src/intTest/resources/application.yml index 3de298a1..b40fcd91 100644 --- a/src/intTest/resources/application.yml +++ b/src/intTest/resources/application.yml @@ -19,6 +19,7 @@ hideout: spring: flyway: enabled: true + clean-disabled: false datasource: driver-class-name: org.h2.Driver url: "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=true;TRACE_LEVEL_FILE=4;" From 1a3333d5f82c328d669e4a3444f4066021ba2578 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 00:52:05 +0900 Subject: [PATCH 061/116] =?UTF-8?q?chore:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E6=99=82=E3=81=AE=E3=83=AD=E3=82=B0=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a5d52f43..7a5f4242 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,4 @@ -import org.gradle.api.tasks.testing.logging.TestExceptionFormat -import org.gradle.api.tasks.testing.logging.TestLogEvent + import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.openapitools.generator.gradle.plugin.tasks.GenerateTask @@ -51,10 +50,6 @@ val integrationTest = task("integrationTest") { shouldRunAfter("test") useJUnitPlatform() - - testLogging { - events("passed") - } } tasks.check { dependsOn(integrationTest) } @@ -69,21 +64,6 @@ tasks.withType { "--add-opens", "java.base/java.lang=ALL-UNNAMED" ).toMutableList() } - testLogging { - events( - TestLogEvent.FAILED, - TestLogEvent.SKIPPED, - TestLogEvent.PASSED, - TestLogEvent.STANDARD_OUT, - TestLogEvent.STANDARD_ERROR, - TestLogEvent.STARTED - ) - exceptionFormat = TestExceptionFormat.FULL - showCauses = true - showExceptions = true - showStackTraces = true - setShowStandardStreams(true) - } } tasks.withType>().configureEach { From 717756deac10489e9a6665ee7b95b897ad41f36c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 01:19:22 +0900 Subject: [PATCH 062/116] =?UTF-8?q?fix:=20=E3=83=AA=E3=83=A2=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=81=AE=E3=83=A1=E3=83=87=E3=82=A3=E3=82=A2=E3=82=92?= =?UTF-8?q?=E5=8F=96=E5=BE=97=E3=81=99=E3=82=8B=E3=81=A8=E3=81=8D=E3=81=AB?= =?UTF-8?q?ResourceResolver=E3=82=92=E4=BD=BF=E7=94=A8=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../media/RemoteMediaDownloadServiceImpl.kt | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMediaDownloadServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMediaDownloadServiceImpl.kt index 6bc9040c..bd1014f8 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMediaDownloadServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMediaDownloadServiceImpl.kt @@ -1,10 +1,6 @@ package dev.usbharu.hideout.core.service.media -import io.ktor.client.* -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.http.* -import io.ktor.utils.io.jvm.javaio.* +import dev.usbharu.hideout.core.service.resource.KtorResourceResolveService import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import java.nio.file.Files @@ -12,16 +8,16 @@ import java.nio.file.Path import kotlin.io.path.outputStream @Service -class RemoteMediaDownloadServiceImpl(private val httpClient: HttpClient) : RemoteMediaDownloadService { +class RemoteMediaDownloadServiceImpl(private val resourceResolveService: KtorResourceResolveService) : + RemoteMediaDownloadService { override suspend fun download(url: String): Path { logger.info("START Download remote file. url: {}", url) - val httpResponse = httpClient.get(url) - httpResponse.contentLength() + val httpResponse = resourceResolveService.resolve(url).body() val createTempFile = Files.createTempFile("hideout-remote-download", ".tmp") logger.debug("Save to {} url: {} ", createTempFile, url) - httpResponse.bodyAsChannel().toInputStream().use { inputStream -> + httpResponse.use { inputStream -> createTempFile.outputStream().use { inputStream.transferTo(it) } From 4ab2fe1fa37d1f19cb81283e15f2f59d9bac76ec Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 01:28:44 +0900 Subject: [PATCH 063/116] =?UTF-8?q?feat:=20=E3=83=AA=E3=83=A2=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=81=AE=E3=83=A1=E3=83=87=E3=82=A3=E3=82=A2=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E5=89=8D=E3=81=AB=E9=87=8D=E8=A4=87=E3=83=81=E3=82=A7?= =?UTF-8?q?=E3=83=83=E3=82=AF=E3=82=92=E5=AE=9F=E6=96=BD=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exposedquery/MediaQueryServiceImpl.kt | 15 ++++++++++++--- .../exposedrepository/MediaRepositoryImpl.kt | 6 +++--- .../hideout/core/query/MediaQueryService.kt | 1 + .../core/service/media/MediaServiceImpl.kt | 12 +++++++++++- src/main/resources/db/migration/V1__Init_DB.sql | 6 +++--- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/MediaQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/MediaQueryServiceImpl.kt index 473c5f66..7b5a5d8e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/MediaQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/MediaQueryServiceImpl.kt @@ -1,17 +1,20 @@ package dev.usbharu.hideout.core.infrastructure.exposedquery -import dev.usbharu.hideout.core.domain.model.media.Media +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Media import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsMedia import dev.usbharu.hideout.core.infrastructure.exposedrepository.toMedia import dev.usbharu.hideout.core.query.MediaQueryService +import dev.usbharu.hideout.util.singleOr import org.jetbrains.exposed.sql.innerJoin import org.jetbrains.exposed.sql.select import org.springframework.stereotype.Repository +import dev.usbharu.hideout.core.domain.model.media.Media as MediaEntity @Repository class MediaQueryServiceImpl : MediaQueryService { - override suspend fun findByPostId(postId: Long): List { - return dev.usbharu.hideout.core.infrastructure.exposedrepository.Media.innerJoin( + override suspend fun findByPostId(postId: Long): List { + return Media.innerJoin( PostsMedia, onColumn = { id }, otherColumn = { mediaId } @@ -19,4 +22,10 @@ class MediaQueryServiceImpl : MediaQueryService { .select { PostsMedia.postId eq postId } .map { it.toMedia() } } + + override suspend fun findByRemoteUrl(remoteUrl: String): MediaEntity { + return Media.select { Media.remoteUrl eq remoteUrl } + .singleOr { FailedToGetResourcesException("remoteUrl: $remoteUrl is duplicate or not exist.", it) } + .toMedia() + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt index 0206feb9..97b7c527 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt @@ -99,9 +99,9 @@ fun ResultRow.toMediaOrNull(): EntityMedia? { object Media : Table("media") { val id = long("id") val name = varchar("name", 255) - val url = varchar("url", 255) - val remoteUrl = varchar("remote_url", 255).nullable() - val thumbnailUrl = varchar("thumbnail_url", 255).nullable() + val url = varchar("url", 255).uniqueIndex() + val remoteUrl = varchar("remote_url", 255).uniqueIndex().nullable() + val thumbnailUrl = varchar("thumbnail_url", 255).uniqueIndex().nullable() val type = integer("type") val blurhash = varchar("blurhash", 255).nullable() val mimeType = varchar("mime_type", 255) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/query/MediaQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/core/query/MediaQueryService.kt index fc7fb675..876c2f1e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/query/MediaQueryService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/query/MediaQueryService.kt @@ -4,4 +4,5 @@ import dev.usbharu.hideout.core.domain.model.media.Media interface MediaQueryService { suspend fun findByPostId(postId: Long): List + suspend fun findByRemoteUrl(remoteUrl: String): Media } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt index 5337e53a..80d26318 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt @@ -1,9 +1,11 @@ package dev.usbharu.hideout.core.service.media +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.exception.media.MediaSaveException import dev.usbharu.hideout.core.domain.exception.media.UnsupportedMediaException import dev.usbharu.hideout.core.domain.model.media.Media import dev.usbharu.hideout.core.domain.model.media.MediaRepository +import dev.usbharu.hideout.core.query.MediaQueryService import dev.usbharu.hideout.core.service.media.converter.MediaProcessService import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest import dev.usbharu.hideout.util.withDelete @@ -22,7 +24,8 @@ class MediaServiceImpl( private val mediaRepository: MediaRepository, private val mediaProcessServices: List, private val remoteMediaDownloadService: RemoteMediaDownloadService, - private val renameService: MediaFileRenameService + private val renameService: MediaFileRenameService, + private val mediaQueryService: MediaQueryService ) : MediaService { @Suppress("LongMethod", "NestedBlockDepth") override suspend fun uploadLocalMedia(mediaRequest: MediaRequest): EntityMedia { @@ -99,6 +102,13 @@ class MediaServiceImpl( override suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media { logger.info("MEDIA Remote media. filename:${remoteMedia.name} url:${remoteMedia.url}") + try { + val findByRemoteUrl = mediaQueryService.findByRemoteUrl(remoteMedia.url) + logger.warn("DUPLICATED Remote media is duplicated. url: {}", remoteMedia.url) + return findByRemoteUrl + } catch (_: FailedToGetResourcesException) { + } + remoteMediaDownloadService.download(remoteMedia.url).withDelete().use { val mimeType = fileTypeDeterminationService.fileType(it.path, remoteMedia.name) diff --git a/src/main/resources/db/migration/V1__Init_DB.sql b/src/main/resources/db/migration/V1__Init_DB.sql index 4ea80255..e0188588 100644 --- a/src/main/resources/db/migration/V1__Init_DB.sql +++ b/src/main/resources/db/migration/V1__Init_DB.sql @@ -46,9 +46,9 @@ create table if not exists media ( id bigint primary key, "name" varchar(255) not null, - url varchar(255) not null, - remote_url varchar(255) null, - thumbnail_url varchar(255) null, + url varchar(255) not null unique, + remote_url varchar(255) null unique, + thumbnail_url varchar(255) null unique, "type" int not null, blurhash varchar(255) null, mime_type varchar(255) not null, From 0da9d6e9f8024ac03b6bcfbc1568807dc573fa2d Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 01:50:44 +0900 Subject: [PATCH 064/116] =?UTF-8?q?test:=20=E3=83=A6=E3=83=8B=E3=83=BC?= =?UTF-8?q?=E3=82=AF=E3=82=A4=E3=83=B3=E3=83=87=E3=83=83=E3=82=AF=E3=82=B9?= =?UTF-8?q?=E3=81=AE=E5=88=B6=E7=B4=84=E3=81=AB=E5=AF=BE=E5=BF=9C=E3=81=99?= =?UTF-8?q?=E3=82=8B=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/intTest/kotlin/mastodon/media/MediaTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/intTest/kotlin/mastodon/media/MediaTest.kt b/src/intTest/kotlin/mastodon/media/MediaTest.kt index 4c9ee710..43a881cf 100644 --- a/src/intTest/kotlin/mastodon/media/MediaTest.kt +++ b/src/intTest/kotlin/mastodon/media/MediaTest.kt @@ -52,7 +52,7 @@ class MediaTest { @Test fun メディアをアップロードできる() = runTest { - whenever(mediaDataStore.save(any())).doReturn(SuccessSavedMedia("", "", "")) + whenever(mediaDataStore.save(any())).doReturn(SuccessSavedMedia("", "a", "a")) mockMvc .multipart("/api/v1/media") { @@ -73,7 +73,7 @@ class MediaTest { @Test fun write_mediaスコープでメディアをアップロードできる() = runTest { - whenever(mediaDataStore.save(any())).doReturn(SuccessSavedMedia("", "", "")) + whenever(mediaDataStore.save(any())).doReturn(SuccessSavedMedia("", "b", "b")) mockMvc .multipart("/api/v1/media") { From fbfcc8f67a91853817b6811adc29d41212d9aa89 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 01:51:06 +0900 Subject: [PATCH 065/116] =?UTF-8?q?test:=20=E7=B5=90=E5=90=88=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=82=E3=83=A9=E3=83=B3=E3=83=80=E3=83=A0?= =?UTF-8?q?=E9=A0=86=E3=81=A7=E5=AE=9F=E8=A1=8C=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/intTest/resources/junit-platform.properties | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/intTest/resources/junit-platform.properties diff --git a/src/intTest/resources/junit-platform.properties b/src/intTest/resources/junit-platform.properties new file mode 100644 index 00000000..acfa9e5a --- /dev/null +++ b/src/intTest/resources/junit-platform.properties @@ -0,0 +1,2 @@ +junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$Random +junit.jupiter.testmethod.order.default=org.junit.jupiter.api.MethodOrderer$Random From b3fb870cbe1a8e34d05d320aa99324b449cce502 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 01:56:29 +0900 Subject: [PATCH 066/116] =?UTF-8?q?test:=20=E5=AD=98=E5=9C=A8=E3=81=97?= =?UTF-8?q?=E3=81=AA=E3=81=84=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=AE?= =?UTF-8?q?=E5=90=8D=E5=89=8D=E3=82=92=E8=A4=87=E9=9B=91=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=A6=E3=81=86=E3=81=A3=E3=81=8B=E3=82=8A=E8=A2=AB=E3=81=A3?= =?UTF-8?q?=E3=81=9F=E3=82=8A=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt b/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt index abee25d6..8e0b0294 100644 --- a/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt +++ b/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt @@ -55,7 +55,7 @@ class WebFingerTest { @Test fun `webfinger 存在しないユーザーに404`() { mockMvc - .get("/.well-known/webfinger?resource=acct:test-user@example.com") + .get("/.well-known/webfinger?resource=acct:invalid-user-notfound-afdjashfal@example.com") .andExpect { status { isNotFound() } } } From 56458fc53c6445b93aeda48513d669dd2d32bd25 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 13:15:02 +0900 Subject: [PATCH 067/116] =?UTF-8?q?refactor:=20Spring=20Security=E3=81=AE?= =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=95=E3=82=A3=E3=82=B0=E3=82=92Kotlin=20?= =?UTF-8?q?DSL=E3=81=A7=E8=A8=98=E8=BF=B0=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/config/SecurityConfig.kt | 148 +++++++++--------- .../util/SpringSecurityKotlinDslExtension.kt | 13 ++ src/main/resources/application.yml | 2 +- 3 files changed, 84 insertions(+), 79 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/util/SpringSecurityKotlinDslExtension.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index 837c1ea6..fb2e86ec 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -13,28 +13,29 @@ import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.Htt import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.UserDetailsImpl import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.util.RsaUtil +import dev.usbharu.hideout.util.hasAnyScope import dev.usbharu.httpsignature.sign.RsaSha256HttpSignatureSigner import dev.usbharu.httpsignature.verify.DefaultSignatureHeaderParser import dev.usbharu.httpsignature.verify.RsaSha256HttpSignatureVerifier import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer -import org.springframework.boot.autoconfigure.security.servlet.PathRequest import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Primary import org.springframework.core.annotation.Order -import org.springframework.http.HttpMethod +import org.springframework.http.HttpMethod.GET +import org.springframework.http.HttpMethod.POST import org.springframework.http.HttpStatus import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter import org.springframework.security.authentication.AccountStatusUserDetailsChecker import org.springframework.security.authentication.AuthenticationManager -import org.springframework.security.config.Customizer import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity +import org.springframework.security.config.annotation.web.invoke import org.springframework.security.config.http.SessionCreationPolicy import org.springframework.security.core.Authentication import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder @@ -51,15 +52,14 @@ import org.springframework.security.web.authentication.AuthenticationEntryPointF import org.springframework.security.web.authentication.HttpStatusEntryPoint import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider -import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher +import org.springframework.security.web.savedrequest.RequestCacheAwareFilter import org.springframework.security.web.util.matcher.AnyRequestMatcher -import org.springframework.web.servlet.handler.HandlerMappingIntrospector import java.security.KeyPairGenerator import java.security.interfaces.RSAPrivateKey import java.security.interfaces.RSAPublicKey import java.util.* -@EnableWebSecurity(debug = false) +@EnableWebSecurity(debug = true) @Configuration @Suppress("FunctionMaxLength", "TooManyFunctions") class SecurityConfig { @@ -75,40 +75,26 @@ class SecurityConfig { @Order(1) fun httpSignatureFilterChain( http: HttpSecurity, - httpSignatureFilter: HttpSignatureFilter, - introspector: HandlerMappingIntrospector + httpSignatureFilter: HttpSignatureFilter ): SecurityFilterChain { - val builder = MvcRequestMatcher.Builder(introspector) - http - .securityMatcher("/users/*/posts/*") - .addFilter(httpSignatureFilter) - .addFilterBefore( - ExceptionTranslationFilter(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)), - HttpSignatureFilter::class.java - ) - .authorizeHttpRequests { - it.requestMatchers( - builder.pattern("/inbox"), - builder.pattern("/outbox"), - builder.pattern("/users/*/inbox"), - builder.pattern("/users/*/outbox") - ).authenticated() - it.anyRequest().permitAll() + http { + securityMatcher("/users/*/posts/*") + addFilterAt(httpSignatureFilter) + addFilterBefore(ExceptionTranslationFilter(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))) + authorizeHttpRequests { + authorize(anyRequest, permitAll) } - .csrf { - it.disable() - } - .exceptionHandling { - it.authenticationEntryPoint(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) - it.defaultAuthenticationEntryPointFor( + exceptionHandling { + authenticationEntryPoint = HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED) + defaultAuthenticationEntryPointFor( HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), AnyRequestMatcher.INSTANCE ) } - .sessionManagement { - it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + sessionManagement { + sessionCreationPolicy = SessionCreationPolicy.STATELESS } - + } return http.build() } @@ -152,59 +138,65 @@ class SecurityConfig { @Bean @Order(2) - fun oauth2SecurityFilterChain(http: HttpSecurity, introspector: HandlerMappingIntrospector): SecurityFilterChain { - val builder = MvcRequestMatcher.Builder(introspector) - + fun oauth2SecurityFilterChain(http: HttpSecurity): SecurityFilterChain { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http) - http.exceptionHandling { - it.authenticationEntryPoint( - LoginUrlAuthenticationEntryPoint("/login") - ) - }.oauth2ResourceServer { - it.jwt(Customizer.withDefaults()) + http { + exceptionHandling { + authenticationEntryPoint = LoginUrlAuthenticationEntryPoint("/login") + } + oauth2ResourceServer { + jwt { + + } + } } return http.build() } @Bean @Order(4) - fun defaultSecurityFilterChain(http: HttpSecurity, introspector: HandlerMappingIntrospector): SecurityFilterChain { - val builder = MvcRequestMatcher.Builder(introspector) + fun defaultSecurityFilterChain(http: HttpSecurity): SecurityFilterChain { + http { + authorizeHttpRequests { - http.authorizeHttpRequests { - it.requestMatchers(PathRequest.toH2Console()).permitAll() - it.requestMatchers( - builder.pattern("/inbox"), - builder.pattern("/users/*/inbox"), - builder.pattern("/api/v1/apps"), - builder.pattern("/api/v1/instance/**"), - builder.pattern("/.well-known/**"), - builder.pattern("/error"), - builder.pattern("/nodeinfo/2.0"), - builder.pattern("/api/v1/accounts") - ).permitAll() - it.requestMatchers( - builder.pattern("/auth/**") - ).anonymous() - it.requestMatchers(builder.pattern("/change-password")).authenticated() - it.requestMatchers(builder.pattern("/api/v1/accounts/verify_credentials")) - .hasAnyAuthority("SCOPE_read", "SCOPE_read:accounts") - it.requestMatchers(builder.pattern(HttpMethod.POST, "/api/v1/media")) - .hasAnyAuthority("SCOPE_write", "SCOPE_write:media") - it.requestMatchers(builder.pattern(HttpMethod.POST, "/api/v1/statuses")) - .hasAnyAuthority("SCOPE_write", "SCOPE_write:statuses") - it.anyRequest().authenticated() - } - http.oauth2ResourceServer { - it.jwt(Customizer.withDefaults()) - }.passwordManagement { }.formLogin(Customizer.withDefaults()).csrf { - it.ignoringRequestMatchers(builder.pattern("/users/*/inbox")) - it.ignoringRequestMatchers(builder.pattern(HttpMethod.POST, "/api/v1/apps")) - it.ignoringRequestMatchers(builder.pattern("/inbox")) - it.ignoringRequestMatchers(PathRequest.toH2Console()) - }.headers { - it.frameOptions { frameOptionsConfig -> - frameOptionsConfig.sameOrigin() + authorize("/error", permitAll) + authorize("/login", permitAll) + authorize(GET, "/.well-known/**", permitAll) + authorize(GET, "/nodeinfo/2.0", permitAll) + + authorize(POST, "/inbox", permitAll) + authorize(POST, "/users/*/inbox", permitAll) + + authorize(POST, "/api/v1/apps", permitAll) + authorize(GET, "/api/v1/instance/**", permitAll) + authorize(POST, "/api/v1/accounts", permitAll) + + authorize("/auth/sign_up", hasRole("ANONYMOUS")) + + authorize(GET, "/api/v1/accounts/verify_credentials", hasAnyScope("read", "read:accounts")) + + authorize(POST, "/api/v1/media", hasAnyScope("write", "write:media")) + authorize(POST, "/api/v1/statuses", hasAnyScope("write", "write:statuses")) + + authorize(anyRequest, authenticated) + } + + oauth2ResourceServer { + jwt { } + } + + formLogin { + + } + + csrf { + ignoringRequestMatchers("/users/*/inbox", "/inbox", "/api/v1/apps", "/api/v1/accounts") + } + + headers { + frameOptions { + sameOrigin = true + } } } return http.build() diff --git a/src/main/kotlin/dev/usbharu/hideout/util/SpringSecurityKotlinDslExtension.kt b/src/main/kotlin/dev/usbharu/hideout/util/SpringSecurityKotlinDslExtension.kt new file mode 100644 index 00000000..42159643 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/util/SpringSecurityKotlinDslExtension.kt @@ -0,0 +1,13 @@ +package dev.usbharu.hideout.util + +import org.springframework.security.authorization.AuthorizationManager +import org.springframework.security.config.annotation.web.AuthorizeHttpRequestsDsl +import org.springframework.security.web.access.intercept.RequestAuthorizationContext + +fun AuthorizeHttpRequestsDsl.hasScope(scope: String): AuthorizationManager = + hasAuthority("SCOPE_$scope") + +fun AuthorizeHttpRequestsDsl.hasAnyScope(vararg scopes: String): AuthorizationManager = + hasAnyAuthority( + *scopes.map { "SCOPE_$it" }.toTypedArray() + ) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index daff34db..9565e8cd 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -36,7 +36,7 @@ spring: max-request-size: 40MB h2: console: - enabled: true + enabled: false server: tomcat: basedir: tomcat From c5c2a2508abeb3f4f30bd2d45160b9f4a73717cf Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 18:49:29 +0900 Subject: [PATCH 068/116] =?UTF-8?q?chore:=20Github=20Actions=E3=82=92?= =?UTF-8?q?=E4=B8=A6=E5=88=97=E5=AE=9F=E8=A1=8C=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/coverage.yml | 34 --- .github/workflows/integration-test.yml | 67 ----- .../workflows/pull-request-merge-check.yml | 237 ++++++++++++++++++ .github/workflows/test.yml | 63 ----- 4 files changed, 237 insertions(+), 164 deletions(-) delete mode 100644 .github/workflows/coverage.yml delete mode 100644 .github/workflows/integration-test.yml create mode 100644 .github/workflows/pull-request-merge-check.yml delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index 6bb91364..00000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Coverage - -on: - pull_request: -permissions: - pull-requests: write - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - - name: Run JUnit - uses: gradle/gradle-build-action@v2.8.1 - with: - arguments: koverXmlReport -x integrationTest - - name: Add coverage report to PR - if: always() - id: kover - uses: mi-kas/kover-report@v1 - with: - path: | - ${{ github.workspace }}/build/reports/kover/report.xml - token: ${{ secrets.GITHUB_TOKEN }} - title: Code Coverage - update-comment: true - min-coverage-overall: 80 - min-coverage-changed-files: 80 - coverage-counter-type: LINE diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml deleted file mode 100644 index ca866978..00000000 --- a/.github/workflows/integration-test.yml +++ /dev/null @@ -1,67 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. -# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle - -name: Integration Test - -on: - pull_request: - branches: [ "develop" ] - -permissions: - contents: read - checks: write - id-token: write - -jobs: - integration-test: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Cache - uses: actions/cache@v3.3.2 - with: - path: ~/.gradle/wrapper - key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} - - name: Cache - uses: actions/cache@v3.3.2 - with: - path: | - ~/.gradle/caches/jars-* - ~/.gradle/caches/transforms-* - ~/.gradle/caches/modules-* - key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} - restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- - - name: Cache - uses: actions/cache@v3.3.2 - with: - path: | - ~/.gradle/caches/build-cache-* - ~/.gradle/caches/[0-9]*.* - .gradle - key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} - restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - - name: MongoDB in GitHub Actions - uses: supercharge/mongodb-github-action@1.10.0 - with: - mongodb-version: latest - - name: Run Integration Test - uses: gradle/gradle-build-action@v2.8.1 - with: - arguments: integrationTest - - name: Publish Test Report - uses: mikepenz/action-junit-report@v2 - if: always() - with: - report_paths: '**/build/test-results/integrationTest/TEST-*.xml' diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml new file mode 100644 index 00000000..035205cd --- /dev/null +++ b/.github/workflows/pull-request-merge-check.yml @@ -0,0 +1,237 @@ +on: + pull_request: + branches: + - "develop" + + +permissions: + contents: read + checks: write + id-token: write + pull-requests: write + +jobs: + setup: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Gradle Wrapper Cache + uses: actions/cache@v3.3.2 + with: + path: ~/.gradle/wrapper + key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} + + - name: Dependencies Cache + uses: actions/cache@v3.3.2 + with: + path: | + ~/.gradle/cache/jars-* + ~/.gradle/caches/transforms-* + ~/.gradle/caches/modules-* + key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} + restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- + + - name: Cache + uses: actions/cache@v3.3.2 + with: + path: | + ~/.gradle/caches/build-cache-* + ~/.gradle/caches/[0-9]*.* + .gradle + key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- + + - name: Build Cache + uses: actions/cache@v3.3.2 + with: + path: | + build + key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }} + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + - name: Build + uses: gradle/gradle-build-action@2.8.1 + with: + arguments: testClasses + + unit-test: + needs: [ setup ] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Gradle Wrapper Cache + uses: actions/cache@v3.3.2 + with: + path: ~/.gradle/wrapper + key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} + + - name: Dependencies Cache + uses: actions/cache@v3.3.2 + with: + path: | + ~/.gradle/cache/jars-* + ~/.gradle/caches/transforms-* + ~/.gradle/caches/modules-* + key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} + restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- + + - name: Cache + uses: actions/cache@v3.3.2 + with: + path: | + ~/.gradle/caches/build-cache-* + ~/.gradle/caches/[0-9]*.* + .gradle + key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- + + - name: Build Cache + uses: actions/cache@v3.3.2 + with: + path: | + build + key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }} + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Unit Test + uses: gradle/gradle-build-action@2.8.1 + with: + arguments: test + + integration-test: + needs: [ setup ] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Gradle Wrapper Cache + uses: actions/cache@v3.3.2 + with: + path: ~/.gradle/wrapper + key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} + + - name: Dependencies Cache + uses: actions/cache@v3.3.2 + with: + path: | + ~/.gradle/cache/jars-* + ~/.gradle/caches/transforms-* + ~/.gradle/caches/modules-* + key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} + restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- + + - name: Cache + uses: actions/cache@v3.3.2 + with: + path: | + ~/.gradle/caches/build-cache-* + ~/.gradle/caches/[0-9]*.* + .gradle + key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- + + - name: Build Cache + uses: actions/cache@v3.3.2 + with: + path: | + build + key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }} + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Unit Test + uses: gradle/gradle-build-action@2.8.1 + with: + arguments: integrationTest + + coverage: + needs: [ setup ] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Gradle Wrapper Cache + uses: actions/cache@v3.3.2 + with: + path: ~/.gradle/wrapper + key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} + + - name: Dependencies Cache + uses: actions/cache@v3.3.2 + with: + path: | + ~/.gradle/cache/jars-* + ~/.gradle/caches/transforms-* + ~/.gradle/caches/modules-* + key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} + restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- + + - name: Cache + uses: actions/cache@v3.3.2 + with: + path: | + ~/.gradle/caches/build-cache-* + ~/.gradle/caches/[0-9]*.* + .gradle + key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- + + - name: Build Cache + uses: actions/cache@v3.3.2 + with: + path: | + build + key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }} + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Run Kover + uses: gradle/gradle-build-action@v2.8.1 + with: + arguments: koverXmlReport -x integrationTest + + - name: Add coverage report to PR + if: always() + id: kover + uses: mi-kas/kover-report@v1 + with: + path: | + ${{ github.workspace }}/build/reports/kover/report.xml + token: ${{ secrets.GITHUB_TOKEN }} + title: Code Coverage + update-comment: true + min-coverage-overall: 80 + min-coverage-changed-files: 80 + coverage-counter-type: LINE + + report-tests: + if: success() || failure() + runs-on: ubuntu-latest + steps: + - name: JUnit Test Report + uses: mikepenz/action-junit-report@v2 + with: + report_paths: '**/build/test-results/*/TEST-*.xml' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index dca2a97b..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,63 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. -# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle - -name: Test - -on: - pull_request: - branches: [ "develop" ] - -permissions: - contents: read - checks: write - id-token: write - -jobs: - test: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Cache - uses: actions/cache@v3.3.2 - with: - path: ~/.gradle/wrapper - key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} - - name: Cache - uses: actions/cache@v3.3.2 - with: - path: | - ~/.gradle/caches/jars-* - ~/.gradle/caches/transforms-* - ~/.gradle/caches/modules-* - key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} - restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- - - name: Cache - uses: actions/cache@v3.3.2 - with: - path: | - ~/.gradle/caches/build-cache-* - ~/.gradle/caches/[0-9]*.* - .gradle - key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} - restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - - name: Run JUnit - uses: gradle/gradle-build-action@v2.8.1 - with: - arguments: test - - name: Publish Test Report - uses: mikepenz/action-junit-report@v2 - if: always() - with: - report_paths: '**/build/test-results/test/TEST-*.xml' From 9d77e51981e0d65ce696506b04bb8929ed534ed3 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 18:52:42 +0900 Subject: [PATCH 069/116] =?UTF-8?q?chore:=20Github=20Actions=E3=82=92?= =?UTF-8?q?=E4=B8=A6=E5=88=97=E5=AE=9F=E8=A1=8C=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-merge-check.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 035205cd..dccb5844 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -56,7 +56,7 @@ jobs: java-version: '17' distribution: 'temurin' - name: Build - uses: gradle/gradle-build-action@2.8.1 + uses: gradle/gradle-build-action@v2.8.1 with: arguments: testClasses @@ -107,7 +107,7 @@ jobs: distribution: 'temurin' - name: Unit Test - uses: gradle/gradle-build-action@2.8.1 + uses: gradle/gradle-build-action@v2.8.1 with: arguments: test @@ -158,7 +158,7 @@ jobs: distribution: 'temurin' - name: Unit Test - uses: gradle/gradle-build-action@2.8.1 + uses: gradle/gradle-build-action@v2.8.1 with: arguments: integrationTest From 708300201f5aa1d06ed619aee27045448f099bec Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 19:22:00 +0900 Subject: [PATCH 070/116] =?UTF-8?q?chore:=20=E7=B5=90=E5=90=88=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E4=BF=AE=E6=AD=A3=E3=80=81=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=83=AC=E3=83=9D=E3=83=BC=E3=83=88=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-merge-check.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index dccb5844..4ff14388 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -157,6 +157,11 @@ jobs: java-version: '17' distribution: 'temurin' + - name: MongoDB in GitHub Actions + uses: supercharge/mongodb-github-action@1.10.0 + with: + mongodb-version: latest + - name: Unit Test uses: gradle/gradle-build-action@v2.8.1 with: @@ -229,6 +234,7 @@ jobs: report-tests: if: success() || failure() + needs: [ unit-test,integration-test ] runs-on: ubuntu-latest steps: - name: JUnit Test Report From ef3d8abe6a2f5a2aa873a218decbc2bb14fd0b3b Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 19:28:03 +0900 Subject: [PATCH 071/116] =?UTF-8?q?chore:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=83=AC=E3=83=9D=E3=83=BC=E3=83=88=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-merge-check.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 4ff14388..706740e8 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -237,6 +237,12 @@ jobs: needs: [ unit-test,integration-test ] runs-on: ubuntu-latest steps: + - name: Build Cache + uses: actions/cache@v3.3.2 + with: + path: | + build + key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }} - name: JUnit Test Report uses: mikepenz/action-junit-report@v2 with: From bcda472c26555948f543cdb259dab6f58500551e Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 19:35:39 +0900 Subject: [PATCH 072/116] =?UTF-8?q?chore:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=83=AC=E3=83=9D=E3=83=BC=E3=83=88=E3=82=92=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=20Lint=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/lint.yml | 61 ------------------- .../workflows/pull-request-merge-check.yml | 59 +++++++++++++++++- 2 files changed, 58 insertions(+), 62 deletions(-) delete mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index 745cde8e..00000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,61 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. -# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle - -name: Lint - -on: - pull_request: - branches: [ "develop" ] - -permissions: - contents: read - pull-requests: write - -jobs: - lint: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Cache - uses: actions/cache@v3.3.2 - with: - path: ~/.gradle/wrapper - key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} - - name: Cache - uses: actions/cache@v3.3.2 - with: - path: | - ~/.gradle/caches/jars-* - ~/.gradle/caches/transforms-* - ~/.gradle/caches/modules-* - key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} - restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- - - name: Cache - uses: actions/cache@v3.3.2 - with: - path: | - ~/.gradle/caches/build-cache-* - ~/.gradle/caches/[0-9]*.* - .gradle - key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} - restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - - name: Build with Gradle - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 - with: - arguments: detektMain - - name: "reviewdog-suggester: Suggest any code changes based on diff with reviewdog" - if: ${{ always() }} - uses: reviewdog/action-suggester@v1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 706740e8..470ba937 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -246,4 +246,61 @@ jobs: - name: JUnit Test Report uses: mikepenz/action-junit-report@v2 with: - report_paths: '**/build/test-results/*/TEST-*.xml' + report_paths: '**/TEST-*.xml' + + lint: + needs: [ setup ] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Gradle Wrapper Cache + uses: actions/cache@v3.3.2 + with: + path: ~/.gradle/wrapper + key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} + + - name: Dependencies Cache + uses: actions/cache@v3.3.2 + with: + path: | + ~/.gradle/cache/jars-* + ~/.gradle/caches/transforms-* + ~/.gradle/caches/modules-* + key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} + restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- + + - name: Cache + uses: actions/cache@v3.3.2 + with: + path: | + ~/.gradle/caches/build-cache-* + ~/.gradle/caches/[0-9]*.* + .gradle + key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- + + - name: Build Cache + uses: actions/cache@v3.3.2 + with: + path: | + build + key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }} + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Build with Gradle + uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 + with: + arguments: detektMain + + - name: "reviewdog-suggester: Suggest any code changes based on diff with reviewdog" + if: ${{ always() }} + uses: reviewdog/action-suggester@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} From d905583f01be31ac6e0908ddfdab809a6b288543 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 19:51:57 +0900 Subject: [PATCH 073/116] =?UTF-8?q?chore:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=83=AC=E3=83=9D=E3=83=BC=E3=83=88=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflows/pull-request-merge-check.yml | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 470ba937..5be87016 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -111,6 +111,12 @@ jobs: with: arguments: test + - name: Save Test Report + uses: actions/cache/save@v3 + with: + path: build/test-results + key: unit-test-report-${{ github.sha }} + integration-test: needs: [ setup ] runs-on: ubuntu-latest @@ -167,6 +173,12 @@ jobs: with: arguments: integrationTest + - name: Save Test Report + uses: actions/cache/save@v3 + with: + path: build/test-results + key: integration-test-report-${{ github.sha }} + coverage: needs: [ setup ] runs-on: ubuntu-latest @@ -237,12 +249,18 @@ jobs: needs: [ unit-test,integration-test ] runs-on: ubuntu-latest steps: - - name: Build Cache - uses: actions/cache@v3.3.2 + - name: Restore Test Report + uses: actions/cache/restore@v3 with: - path: | - build - key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }} + path: build/test-results + key: unit-test-report-${{ github.sha }} + + - name: Restore Test Report + uses: actions/cache/restore@v3 + with: + path: build/test-results + key: integration-test-report-${{ github.sha }} + - name: JUnit Test Report uses: mikepenz/action-junit-report@v2 with: From b1d6cbfdb6efdf201a02180b55f4c466b5a009ef Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 20:01:25 +0900 Subject: [PATCH 074/116] =?UTF-8?q?chore:=20=E3=82=AD=E3=83=A3=E3=83=83?= =?UTF-8?q?=E3=82=B7=E3=83=A5=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflows/pull-request-merge-check.yml | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 5be87016..2e87144e 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -30,8 +30,8 @@ jobs: ~/.gradle/cache/jars-* ~/.gradle/caches/transforms-* ~/.gradle/caches/modules-* - key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} - restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- + key: gradle-dependencies-${{ hashFiles('**/*.gradle.kts') }} + restore-keys: gradle-dependencies- - name: Cache uses: actions/cache@v3.3.2 @@ -40,8 +40,8 @@ jobs: ~/.gradle/caches/build-cache-* ~/.gradle/caches/[0-9]*.* .gradle - key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} - restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- + key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}- - name: Build Cache uses: actions/cache@v3.3.2 @@ -80,8 +80,8 @@ jobs: ~/.gradle/cache/jars-* ~/.gradle/caches/transforms-* ~/.gradle/caches/modules-* - key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} - restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- + key: gradle-dependencies-${{ hashFiles('**/*.gradle.kts') }} + restore-keys: gradle-dependencies- - name: Cache uses: actions/cache@v3.3.2 @@ -90,8 +90,8 @@ jobs: ~/.gradle/caches/build-cache-* ~/.gradle/caches/[0-9]*.* .gradle - key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} - restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- + key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}- - name: Build Cache uses: actions/cache@v3.3.2 @@ -137,8 +137,8 @@ jobs: ~/.gradle/cache/jars-* ~/.gradle/caches/transforms-* ~/.gradle/caches/modules-* - key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} - restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- + key: gradle-dependencies-${{ hashFiles('**/*.gradle.kts') }} + restore-keys: gradle-dependencies- - name: Cache uses: actions/cache@v3.3.2 @@ -147,8 +147,8 @@ jobs: ~/.gradle/caches/build-cache-* ~/.gradle/caches/[0-9]*.* .gradle - key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} - restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- + key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}- - name: Build Cache uses: actions/cache@v3.3.2 @@ -199,8 +199,8 @@ jobs: ~/.gradle/cache/jars-* ~/.gradle/caches/transforms-* ~/.gradle/caches/modules-* - key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} - restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- + key: gradle-dependencies-${{ hashFiles('**/*.gradle.kts') }} + restore-keys: gradle-dependencies- - name: Cache uses: actions/cache@v3.3.2 @@ -209,8 +209,8 @@ jobs: ~/.gradle/caches/build-cache-* ~/.gradle/caches/[0-9]*.* .gradle - key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} - restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- + key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}- - name: Build Cache uses: actions/cache@v3.3.2 @@ -286,8 +286,8 @@ jobs: ~/.gradle/cache/jars-* ~/.gradle/caches/transforms-* ~/.gradle/caches/modules-* - key: gradle-dependencies-${{ steps.cache-key.outputs.week }}-${{ hashFiles('**/*.gradle.kts') }} - restore-keys: gradle-dependencies-${{ steps.cache-key.outputs.week }}- + key: gradle-dependencies-${{ hashFiles('**/*.gradle.kts') }} + restore-keys: gradle-dependencies- - name: Cache uses: actions/cache@v3.3.2 @@ -296,8 +296,8 @@ jobs: ~/.gradle/caches/build-cache-* ~/.gradle/caches/[0-9]*.* .gradle - key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}-${{ github.sha }} - restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ steps.cache-key.outputs.week }}- + key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}- - name: Build Cache uses: actions/cache@v3.3.2 From 479181e5b7b598941f775f541d2ace403855dd15 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 20:04:30 +0900 Subject: [PATCH 075/116] =?UTF-8?q?chore:=20job=E3=81=AB=E5=90=8D=E5=89=8D?= =?UTF-8?q?=E3=82=92=E3=81=A4=E3=81=91=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-merge-check.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 2e87144e..72f62868 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -1,3 +1,5 @@ +name: PullRequest Merge Check + on: pull_request: branches: @@ -12,6 +14,7 @@ permissions: jobs: setup: + name: Setup runs-on: ubuntu-latest steps: - name: Checkout @@ -61,6 +64,7 @@ jobs: arguments: testClasses unit-test: + name: Unit Test needs: [ setup ] runs-on: ubuntu-latest steps: @@ -118,6 +122,7 @@ jobs: key: unit-test-report-${{ github.sha }} integration-test: + name: Integration Test needs: [ setup ] runs-on: ubuntu-latest steps: @@ -180,6 +185,7 @@ jobs: key: integration-test-report-${{ github.sha }} coverage: + name: Coverage needs: [ setup ] runs-on: ubuntu-latest steps: @@ -245,6 +251,7 @@ jobs: coverage-counter-type: LINE report-tests: + name: Report Tests if: success() || failure() needs: [ unit-test,integration-test ] runs-on: ubuntu-latest @@ -267,6 +274,7 @@ jobs: report_paths: '**/TEST-*.xml' lint: + name: Lint needs: [ setup ] runs-on: ubuntu-latest steps: From 50415688d52c2ec72091ec40e9bdb988b6b3a12e Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:01:18 +0900 Subject: [PATCH 076/116] =?UTF-8?q?test:=20e2e=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 39 +++++++++-- src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt | 69 ++++++++++++++++++++ src/e2eTest/resources/application.yml | 42 ++++++++++++ src/e2eTest/resources/logback.xml | 11 ++++ 4 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt create mode 100644 src/e2eTest/resources/application.yml create mode 100644 src/e2eTest/resources/logback.xml diff --git a/build.gradle.kts b/build.gradle.kts index 7a5f4242..002d9613 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,3 @@ - import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.openapitools.generator.gradle.plugin.tasks.GenerateTask @@ -32,6 +31,10 @@ sourceSets { compileClasspath += sourceSets.main.get().output runtimeClasspath += sourceSets.main.get().output } + create("e2eTest") { + compileClasspath += sourceSets.main.get().output + runtimeClasspath += sourceSets.main.get().output + } } val intTestImplementation by configurations.getting { @@ -41,6 +44,14 @@ val intTestRuntimeOnly by configurations.getting { extendsFrom(configurations.runtimeOnly.get()) } +val e2eTestImplementation by configurations.getting { + extendsFrom(configurations.implementation.get()) +} + +val e2eTestRuntimeOnly by configurations.getting { + extendsFrom(configurations.runtimeOnly.get()) +} + val integrationTest = task("integrationTest") { description = "Runs integration tests." group = "verification" @@ -52,13 +63,24 @@ val integrationTest = task("integrationTest") { useJUnitPlatform() } -tasks.check { dependsOn(integrationTest) } +val e2eTest = task("e2eTest") { + description = "Runs e2e tests." + group = "verification" + + testClassesDirs = sourceSets["e2eTest"].output.classesDirs + classpath = sourceSets["e2eTest"].runtimeClasspath + shouldRunAfter("test") + + useJUnitPlatform() +} + +tasks.check { + dependsOn(integrationTest) + dependsOn(e2eTest) +} tasks.withType { useJUnitPlatform() - val cpus = Runtime.getRuntime().availableProcessors() -// maxParallelForks = max(1, cpus - 1) -// setForkEvery(4) doFirst { jvmArgs = arrayOf( "--add-opens", "java.base/java.lang=ALL-UNNAMED" @@ -207,6 +229,13 @@ dependencies { intTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4") intTestImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0") + e2eTestImplementation("org.springframework.boot:spring-boot-starter-test") + e2eTestImplementation("org.springframework.security:spring-security-test") + e2eTestImplementation("org.springframework.boot:spring-boot-starter-webflux") + e2eTestImplementation("org.jsoup:jsoup:1.17.1") + + + } detekt { diff --git a/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt b/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt new file mode 100644 index 00000000..e3c29202 --- /dev/null +++ b/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt @@ -0,0 +1,69 @@ +package oauth2 + +import dev.usbharu.hideout.SpringApplication +import org.jsoup.Jsoup +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation +import org.junit.jupiter.api.Order +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestMethodOrder +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.MediaType +import org.springframework.test.web.reactive.server.WebTestClient +import org.springframework.web.reactive.function.BodyInserters + +@SpringBootTest( + classes = [SpringApplication::class], + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, +) +@TestMethodOrder(OrderAnnotation::class) +class OAuth2LoginTest { + + @Autowired + private lateinit var webTestClient: WebTestClient + + @Test + @Order(2) + fun アカウント作成() { + val returnResult = webTestClient.get() + .uri("/auth/sign_up") + .exchange() + .expectStatus() + .isOk + .returnResult(String::class.java) + + val html = returnResult + .responseBody + .toStream() + .toList() + .toList() + .joinToString("") + + val session = returnResult.responseCookies["JSESSIONID"]?.first()?.value!! + + val attr = Jsoup.parse(html).selectXpath("//input[@name=\"_csrf\"]").attr("value") + + println("CSRF TOKEN = $attr") + + val csrfToken = attr + + webTestClient + .post() + .uri("/api/v1/accounts") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .body( + BodyInserters.fromFormData("username", "oatuh-login-test") + .with("password", "very-secure-password").with("_csrf", csrfToken) + ) + .cookie("JSESSIONID", session) + .exchange() + .expectStatus().isFound + .expectCookie() + + } + +// @Test +// fun `OAuth2で権限read writeを持ったトークンでのログインができる`() { +//// webTestClient.post().uri("/api/v1/apps") +// } +} diff --git a/src/e2eTest/resources/application.yml b/src/e2eTest/resources/application.yml new file mode 100644 index 00000000..b40fcd91 --- /dev/null +++ b/src/e2eTest/resources/application.yml @@ -0,0 +1,42 @@ +hideout: + url: "https://localhost:8080" + use-mongodb: true + security: + jwt: + generate: true + key-id: a + private-key: "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKjMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvuNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulgp2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlRZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwiVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskVlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83HmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwYdgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cwta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2TN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPvt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDUAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISLDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnKxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEAmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfzet6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhrVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicDTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cncdn/RsYEONbwQSjIfMPkvxF+8HQ==" + public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB" + storage: + use-s3: true + endpoint: "http://localhost:8082/test-hideout" + public-url: "http://localhost:8082/test-hideout" + bucket: "test-hideout" + region: "auto" + access-key: "" + secret-key: "" + +spring: + flyway: + enabled: true + clean-disabled: false + datasource: + driver-class-name: org.h2.Driver + url: "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=true;TRACE_LEVEL_FILE=4;" + username: "" + password: + data: + mongodb: + auto-index-creation: true + host: localhost + port: 27017 + database: hideout + h2: + console: + enabled: true + +# exposed: +# generate-ddl: true +# excluded-packages: dev.usbharu.hideout.core.infrastructure.kjobexposed +server: + port: 8080 diff --git a/src/e2eTest/resources/logback.xml b/src/e2eTest/resources/logback.xml new file mode 100644 index 00000000..a8bb21c4 --- /dev/null +++ b/src/e2eTest/resources/logback.xml @@ -0,0 +1,11 @@ + + + + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n + + + + + + + From 6727a1c8da4bf9156b4ba3ee57f8de06a2a38760 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:11:09 +0900 Subject: [PATCH 077/116] =?UTF-8?q?fix:=20POST:=20/api/v1/accounts?= =?UTF-8?q?=E3=81=AB=E5=AF=BE=E3=81=99=E3=82=8B=E3=83=AA=E3=82=AF=E3=82=A8?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=81=ABCSRF=E3=83=88=E3=83=BC=E3=82=AF?= =?UTF-8?q?=E3=83=B3=E3=81=8C=E5=BF=85=E9=A0=88=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/mastodon/account/AccountApiTest.kt | 26 ++++++++++++++++++- .../application/config/SecurityConfig.kt | 2 +- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/intTest/kotlin/mastodon/account/AccountApiTest.kt b/src/intTest/kotlin/mastodon/account/AccountApiTest.kt index b08e2cc5..fb8d66c6 100644 --- a/src/intTest/kotlin/mastodon/account/AccountApiTest.kt +++ b/src/intTest/kotlin/mastodon/account/AccountApiTest.kt @@ -129,12 +129,36 @@ class AccountApiTest { mockMvc .post("/api/v1/accounts") { contentType = MediaType.APPLICATION_FORM_URLENCODED - param("username", "api-test-user-3") + param("username", "api-test-user-4") with(SecurityMockMvcRequestPostProcessors.csrf()) } .andExpect { status { isBadRequest() } } } + @Test + @WithAnonymousUser + fun apiV1AccountsPostでJSONで作ろうとしても400() { + mockMvc + .post("/api/v1/accounts") { + contentType = MediaType.APPLICATION_JSON + content = """{"username":"api-test-user-5","password":"very-very-secure-password"}""" + with(SecurityMockMvcRequestPostProcessors.csrf()) + } + .andExpect { status { isUnsupportedMediaType() } } + } + + @Test + @WithAnonymousUser + fun apiV1AccountsPostにCSRFトークンは必要() { + mockMvc + .post("/api/v1/accounts") { + contentType = MediaType.APPLICATION_FORM_URLENCODED + param("username", "api-test-user-2") + param("password", "very-secure-password") + } + .andExpect { status { isForbidden() } } + } + companion object { @JvmStatic @AfterAll diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index fb2e86ec..be0d6919 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -190,7 +190,7 @@ class SecurityConfig { } csrf { - ignoringRequestMatchers("/users/*/inbox", "/inbox", "/api/v1/apps", "/api/v1/accounts") + ignoringRequestMatchers("/users/*/inbox", "/inbox", "/api/v1/apps") } headers { From 1ad9eb8e88ffa5b24a4c888fbb5fe67462e7f099 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:50:52 +0900 Subject: [PATCH 078/116] =?UTF-8?q?test:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E5=A4=89=E6=95=B0=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt b/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt index e3c29202..723f6f05 100644 --- a/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt +++ b/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt @@ -45,15 +45,13 @@ class OAuth2LoginTest { println("CSRF TOKEN = $attr") - val csrfToken = attr - webTestClient .post() .uri("/api/v1/accounts") .contentType(MediaType.APPLICATION_FORM_URLENCODED) .body( BodyInserters.fromFormData("username", "oatuh-login-test") - .with("password", "very-secure-password").with("_csrf", csrfToken) + .with("password", "very-secure-password").with("_csrf", attr) ) .cookie("JSESSIONID", session) .exchange() From ef40b94b5ed6b9e2cd2cfc89efdaa8f8bcbe4c0d Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 1 Dec 2023 17:32:10 +0900 Subject: [PATCH 079/116] =?UTF-8?q?test:=20e2e=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 + src/e2eTest/kotlin/KarateUtil.kt | 12 +++ src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt | 82 ++++++++----------- src/e2eTest/resources/karate-config.js | 25 ++++++ .../resources/oauth2/Oauth2LoginTest.feature | 32 ++++++++ src/e2eTest/resources/oauth2/test.feature | 9 ++ src/e2eTest/resources/oauth2/user.sql | 46 +++++++++++ 7 files changed, 158 insertions(+), 50 deletions(-) create mode 100644 src/e2eTest/kotlin/KarateUtil.kt create mode 100644 src/e2eTest/resources/karate-config.js create mode 100644 src/e2eTest/resources/oauth2/Oauth2LoginTest.feature create mode 100644 src/e2eTest/resources/oauth2/test.feature create mode 100644 src/e2eTest/resources/oauth2/user.sql diff --git a/build.gradle.kts b/build.gradle.kts index 002d9613..c8d31ca7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -233,6 +233,8 @@ dependencies { e2eTestImplementation("org.springframework.security:spring-security-test") e2eTestImplementation("org.springframework.boot:spring-boot-starter-webflux") e2eTestImplementation("org.jsoup:jsoup:1.17.1") + e2eTestImplementation("com.intuit.karate:karate-junit5:1.4.1") + diff --git a/src/e2eTest/kotlin/KarateUtil.kt b/src/e2eTest/kotlin/KarateUtil.kt new file mode 100644 index 00000000..e78e8510 --- /dev/null +++ b/src/e2eTest/kotlin/KarateUtil.kt @@ -0,0 +1,12 @@ +import com.intuit.karate.junit5.Karate + +object KarateUtil { + fun springBootKarateTest(path: String, scenario: String, clazz: Class<*>, port: String): Karate { + if (scenario.isEmpty()) { + return Karate.run(path).relativeTo(clazz).systemProperty("karate.port", port).karateEnv("dev") + } else { + return Karate.run(path).scenarioName(scenario).relativeTo(clazz).systemProperty("karate.port", port) + .karateEnv("dev") + } + } +} diff --git a/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt b/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt index 723f6f05..ba524c0d 100644 --- a/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt +++ b/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt @@ -1,67 +1,49 @@ package oauth2 +import KarateUtil +import com.intuit.karate.junit5.Karate import dev.usbharu.hideout.SpringApplication -import org.jsoup.Jsoup -import org.junit.jupiter.api.MethodOrderer.OrderAnnotation -import org.junit.jupiter.api.Order -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestMethodOrder +import org.flywaydb.core.Flyway +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.TestFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest -import org.springframework.http.MediaType -import org.springframework.test.web.reactive.server.WebTestClient -import org.springframework.web.reactive.function.BodyInserters +import org.springframework.boot.test.web.server.LocalServerPort +import org.springframework.test.context.jdbc.Sql @SpringBootTest( classes = [SpringApplication::class], webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, ) -@TestMethodOrder(OrderAnnotation::class) +@Sql("/oauth2/user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) class OAuth2LoginTest { - @Autowired - private lateinit var webTestClient: WebTestClient + @LocalServerPort + private var port = "" - @Test - @Order(2) - fun アカウント作成() { - val returnResult = webTestClient.get() - .uri("/auth/sign_up") - .exchange() - .expectStatus() - .isOk - .returnResult(String::class.java) - - val html = returnResult - .responseBody - .toStream() - .toList() - .toList() - .joinToString("") - - val session = returnResult.responseCookies["JSESSIONID"]?.first()?.value!! - - val attr = Jsoup.parse(html).selectXpath("//input[@name=\"_csrf\"]").attr("value") - - println("CSRF TOKEN = $attr") - - webTestClient - .post() - .uri("/api/v1/accounts") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .body( - BodyInserters.fromFormData("username", "oatuh-login-test") - .with("password", "very-secure-password").with("_csrf", attr) - ) - .cookie("JSESSIONID", session) - .exchange() - .expectStatus().isFound - .expectCookie() + @Karate.Test + @TestFactory + fun test(): Karate = + Karate.run("test").scenarioName("invalid").relativeTo(javaClass).systemProperty("karate.port", port) + .karateEnv("dev") + @Karate.Test + @TestFactory + fun `スコープwrite readを持ったトークンの作成`(): Karate { + return KarateUtil.springBootKarateTest( + "Oauth2LoginTest", + "スコープwrite readを持ったトークンの作成", + javaClass, + port + ) } -// @Test -// fun `OAuth2で権限read writeを持ったトークンでのログインができる`() { -//// webTestClient.post().uri("/api/v1/apps") -// } + companion object { + @JvmStatic + @AfterAll + fun dropDatabase(@Autowired flyway: Flyway) { + flyway.clean() + flyway.migrate() + } + } } diff --git a/src/e2eTest/resources/karate-config.js b/src/e2eTest/resources/karate-config.js new file mode 100644 index 00000000..9fd5dd1e --- /dev/null +++ b/src/e2eTest/resources/karate-config.js @@ -0,0 +1,25 @@ +function fn() { + var env = karate.env; // get java system property 'karate.env' + karate.log('karate.env system property was:', env); + if (!env) { + env = 'dev'; // a custom 'intelligent' default + karate.log('karate.env set to "dev" as default.'); + } + let config; + if (env === 'test') { + config = { + baseUrl: 'https://test-hideout.usbharu.dev' + } + } else if (env === 'dev') { + let port = karate.properties['karate.port'] || '8080' + config = { + baseUrl: 'http://localhost:' + port + } + } else { + throw 'Unknown environment [' + env + '].' + } + // don't waste time waiting for a connection or if servers don't respond within 0,3 seconds + karate.configure('connectTimeout', 1000); + karate.configure('readTimeout', 1000); + return config; +} diff --git a/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature b/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature new file mode 100644 index 00000000..29ae760c --- /dev/null +++ b/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature @@ -0,0 +1,32 @@ +Feature: OAuth2 Login Test + + Background: + * url baseUrl + * configure driver = { type: 'chrome' } + + Scenario: スコープwrite readを持ったトークンの作成 + + * def apps = + """ + { + "client_name": "oauth2-test-client-1", + "redirect_uris": "https://example.com", + "scopes": "write read" + } + """ + + Given path '/api/v1/apps' + And request apps + When method post + Then status 200 + + * def client_id = response.client_id + * def client_secret = response.client_secret + + * def authorizeEndpoint = baseUrl + '/oauth/authorize?response_type=code&redirect_uri=https://example.com&client_id=' + client_id + '&scope=write read' + + Given driver authorizeEndpoint + And driver.input('#username','test-user') + And driver.input('#password','password') + When driver.submit().click('body > div > form > button') + Then match driver.title == 'test' diff --git a/src/e2eTest/resources/oauth2/test.feature b/src/e2eTest/resources/oauth2/test.feature new file mode 100644 index 00000000..7a801de8 --- /dev/null +++ b/src/e2eTest/resources/oauth2/test.feature @@ -0,0 +1,9 @@ +Feature: test + + Background: + * url baseUrl + + Scenario: test + Given path '/api/v1/apps' + When method get + Then status 401 diff --git a/src/e2eTest/resources/oauth2/user.sql b/src/e2eTest/resources/oauth2/user.sql new file mode 100644 index 00000000..15aa977f --- /dev/null +++ b/src/e2eTest/resources/oauth2/user.sql @@ -0,0 +1,46 @@ +insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) +VALUES (1730415786666758144, 'test-user', 'localhost', 'Im test user.', 'THis account is test user.', + '$2a$10$/mWC/n7nC7X3l9qCEOKnredxne2zewoqEsJWTOdlKfg2zXKJ0F9Em', 'http://localhost/users/test-user/inbox', + 'http://localhost/users/test-user/outbox', 'http://localhost/users/test-user', + '-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi4mifRg6huAIn6DXk3Vn +5tkRC0AO32ZJvczwXr9xDj4HJvrSUHBAxIwwIeuCceAYtiuZk4JmEKydeB6SRkoO +Nty93XZXS1SMmiHCvWOY5YlpnfFU1kLqW3fkXcLNls4XmzujLt1i2sT8mYkENAsP +h6K4SRtmktOVYZOWcVEcfLGKbJvaDD/+lKikNC1XCouylfGV/bA/FPY5vuI+7cdM +Mjana28JdiWlPWSdzcxtCSgN+nGWPjk2WWm8K+wK2zXqMxA0U0p4odyyILBGALxX +zMqObIQvpwPh/t+b6ohem4eq70/0/SwDhd+IzHkT3x4UzG1oxSQS/juPkO7uuS8p +uwIDAQAB +-----END PUBLIC KEY----- +', + '-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCLiaJ9GDqG4Aif +oNeTdWfm2RELQA7fZkm9zPBev3EOPgcm+tJQcEDEjDAh64Jx4Bi2K5mTgmYQrJ14 +HpJGSg423L3ddldLVIyaIcK9Y5jliWmd8VTWQupbd+Rdws2WzhebO6Mu3WLaxPyZ +iQQ0Cw+HorhJG2aS05Vhk5ZxURx8sYpsm9oMP/6UqKQ0LVcKi7KV8ZX9sD8U9jm+ +4j7tx0wyNqdrbwl2JaU9ZJ3NzG0JKA36cZY+OTZZabwr7ArbNeozEDRTSnih3LIg +sEYAvFfMyo5shC+nA+H+35vqiF6bh6rvT/T9LAOF34jMeRPfHhTMbWjFJBL+O4+Q +7u65Lym7AgMBAAECggEADJLa7v3LbFLsxAGY22NFeRJPTF252VycwXshn9ANbnSd +bWBFqlTrKSrevXe82ekRIP09ygKCkvcS+3t5v9a1gDEU9MtQo2ubfdoT87/xS6G9 +wCs6c1I1Twe3LtG6d9/bVbQiiLsPSNpeTrF/jPcAL780bvYGoK1rNQ85C7383Kl6 +1nwZCD0itjkmzbO0nGMRCduW46OdQKiOMuEC7z0zwynH3cK3wGvdlKyLG4L3pPZm +1/Uz7AZTieqSCjSgcgmaut7dmS49e3j8ujfb3wcKscfHoofyqNWsW1xyU1WytO9a +QLh9wlqfvGlfwQWkY6z6uFmc4XfRVZSC8nic4cAW3QKBgQC4PYbR5AuylDcfc6Am +jpL5mcF6qEMnEPgnL9z5VvuLs1f/JEyx5VgzQreDOKc1KOxDX7Xhok4gpvIJv1fi +zimviszEmIpHdPvgS7mP2hu42bSIjwVaXpny5aEEZbB6HQ9pGDW/MSsgmb6x31Kx +o+sslpqf9cpalI35UPtkNaEJNwKBgQDB4tVUQ5gGPKllEfCN64B/B7wodWr5cUNU +UpUXdFPCu+HXnRen6GKLo+25wmCUGtcIuvCY1Xm+tL0Z7jrI+oOD4CL9ob7BJrPF +XCq0jUhaEzWFGp1SOa6n+35fWPkCfG4EwfsK8+PWoZsZc1eykMxIJmBln3vufuHz +qybfhy0VnQKBgD2tAxvyXmQar9VMjLk7k0IRUa6w80H5sUjVAgFKOA0NLZEQ4sfO +wdbvJ6W66mamW2k2ehmdjs/pcy8GKfKYF2ZXbbMGaYwAQm1UjDr2xb78yi3Iyv70 +mk6wxlVFgW1vmwAQhbWKTSitryO2YeVrvUeA5yRTULk/78Mdc/qY5V7DAoGAAu3I +RzOWMlHsRSiWN66dDE4zm3DaotYBLF7q/aW2NjTcXoNy/ghWpMFfL/UtvE8DfJBG +XiirZCQazy94F90g63cRUD+HQCezg4G2629O7n1ny5DxW3Kfns3/xLT1XgI/Lzc2 +8Z1pja53R1Ukt//T9isOPbrBBoNIKoQlXC8QkUkCgYEAsib3uOMAIOJab5jc8FSj +VG+Cg2H63J5DgUUwx2Y0DPENugdGyYzCDMVPBNaB0Ru1SpqbUjgqh+YHynunSVeu +hDXMOteeyeVHUGw8mvcCEt53uRYVNW/rzXTMqfLVxbsJZHCsJBtFpwcgD2w4NjS2 +Ja15+ZWbOA4vJA9pOh3x4XM= +-----END PRIVATE KEY----- +', 1701398248417, + 'http://localhost/users/test-user#pubkey', 'http://localhost/users/test-user/following', + 'http://localhost/users/test-users/followers', null); From aff9b68480953226961f6873fbfcd1d9c4a94df7 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 1 Dec 2023 17:32:36 +0900 Subject: [PATCH 080/116] =?UTF-8?q?fix:=20OAuth2=E3=81=A7=E3=83=AD?= =?UTF-8?q?=E3=82=B0=E3=82=A4=E3=83=B3=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=8F?= =?UTF-8?q?=E3=81=AA=E3=81=A3=E3=81=A6=E3=81=84=E3=81=9F=E3=81=AE=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/config/SecurityConfig.kt | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index be0d6919..aa60fa16 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -11,12 +11,14 @@ import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.Htt import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureVerifierComposite import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.UserDetailsImpl +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.UserDetailsServiceImpl import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.util.RsaUtil import dev.usbharu.hideout.util.hasAnyScope import dev.usbharu.httpsignature.sign.RsaSha256HttpSignatureSigner import dev.usbharu.httpsignature.verify.DefaultSignatureHeaderParser import dev.usbharu.httpsignature.verify.RsaSha256HttpSignatureVerifier +import jakarta.annotation.PostConstruct import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer @@ -32,6 +34,8 @@ import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter import org.springframework.security.authentication.AccountStatusUserDetailsChecker import org.springframework.security.authentication.AuthenticationManager +import org.springframework.security.authentication.dao.DaoAuthenticationProvider +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity @@ -59,7 +63,8 @@ import java.security.interfaces.RSAPrivateKey import java.security.interfaces.RSAPublicKey import java.util.* -@EnableWebSecurity(debug = true) + +@EnableWebSecurity(debug = false) @Configuration @Suppress("FunctionMaxLength", "TooManyFunctions") class SecurityConfig { @@ -114,6 +119,16 @@ class SecurityConfig { } @Bean + @Order(2) + fun daoAuthenticationProvider(userDetailsServiceImpl: UserDetailsServiceImpl): DaoAuthenticationProvider { + val daoAuthenticationProvider = DaoAuthenticationProvider() + daoAuthenticationProvider.setUserDetailsService(userDetailsServiceImpl) + + return daoAuthenticationProvider + } + + @Bean + @Order(1) fun httpSignatureAuthenticationProvider(transaction: Transaction): PreAuthenticatedAuthenticationProvider { val provider = PreAuthenticatedAuthenticationProvider() val signatureHeaderParser = DefaultSignatureHeaderParser() @@ -269,3 +284,18 @@ data class JwkConfig( val publicKey: String, val privateKey: String ) + + +@Configuration +class PostSecurityConfig( + val auth: AuthenticationManagerBuilder, + val daoAuthenticationProvider: DaoAuthenticationProvider, + val httpSignatureAuthenticationProvider: PreAuthenticatedAuthenticationProvider +) { + + @PostConstruct + fun config() { + auth.authenticationProvider(daoAuthenticationProvider) + auth.authenticationProvider(httpSignatureAuthenticationProvider) + } +} From c1c2e1e8437bc2572e966f99d33745018fbccf05 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 1 Dec 2023 22:59:39 +0900 Subject: [PATCH 081/116] =?UTF-8?q?test:=20OAuth2=E3=81=A7=E3=81=AE?= =?UTF-8?q?=E3=83=88=E3=83=BC=E3=82=AF=E3=83=B3=E4=BD=9C=E6=88=90=E3=81=AE?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/e2eTest/resources/application.yml | 4 ++++ src/e2eTest/resources/logback.xml | 8 +++++++ .../resources/oauth2/Oauth2LoginTest.feature | 24 ++++++++++++++++--- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/e2eTest/resources/application.yml b/src/e2eTest/resources/application.yml index b40fcd91..69e0ebcd 100644 --- a/src/e2eTest/resources/application.yml +++ b/src/e2eTest/resources/application.yml @@ -40,3 +40,7 @@ spring: # excluded-packages: dev.usbharu.hideout.core.infrastructure.kjobexposed server: port: 8080 + tomcat: + basedir: tomcat-e2e + accesslog: + enabled: true diff --git a/src/e2eTest/resources/logback.xml b/src/e2eTest/resources/logback.xml index a8bb21c4..7f19e2eb 100644 --- a/src/e2eTest/resources/logback.xml +++ b/src/e2eTest/resources/logback.xml @@ -1,4 +1,11 @@ + + ./e2eTest.log + + UTF-8 + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n + + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n @@ -6,6 +13,7 @@ + diff --git a/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature b/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature index 29ae760c..582af835 100644 --- a/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature +++ b/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature @@ -10,7 +10,7 @@ Feature: OAuth2 Login Test """ { "client_name": "oauth2-test-client-1", - "redirect_uris": "https://example.com", + "redirect_uris": "https://usbharu.dev", "scopes": "write read" } """ @@ -23,10 +23,28 @@ Feature: OAuth2 Login Test * def client_id = response.client_id * def client_secret = response.client_secret - * def authorizeEndpoint = baseUrl + '/oauth/authorize?response_type=code&redirect_uri=https://example.com&client_id=' + client_id + '&scope=write read' + * def authorizeEndpoint = baseUrl + '/oauth/authorize?response_type=code&redirect_uri=https://usbharu.dev&client_id=' + client_id + '&scope=write%20read' Given driver authorizeEndpoint And driver.input('#username','test-user') And driver.input('#password','password') + When driver.submit().click('body > div > form > button') - Then match driver.title == 'test' + Then driver.waitForUrl(authorizeEndpoint + "&continue") + And driver.click('#read') + And driver.click('#write') + + When driver.submit().click('#submit-consent') + Then driver.waitUntil("location.host == 'usbharu.dev'") + + * def code = script("new URLSearchParams(document.location.search).get('code')") + + Given path '/oauth/token' + And form field client_id = client_id + And form field client_secret = client_secret + And form field redirect_uri = 'https://usbharu.dev' + And form field grant_type = 'authorization_code' + And form field code = code + And form field scope = 'write read' + When method post + Then status 200 From d94099bacfe6f624c439749061352ff7085992b5 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:00:27 +0900 Subject: [PATCH 082/116] =?UTF-8?q?chore:=20gitignore=E3=82=92=E7=B7=A8?= =?UTF-8?q?=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 5f98beae..0d7dbff3 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ out/ /src/main/web/generated/ /stats.html /tomcat/ +/tomcat-e2e/ +/e2eTest.log From 58dbcb810e0012ccb9142299da8a9b3bfe50dd72 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:10:47 +0900 Subject: [PATCH 083/116] =?UTF-8?q?chore:=20=E9=81=8E=E5=89=B0=E3=81=AB?= =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=82=92=E5=87=BA=E5=8A=9B=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=81=84=E3=81=9F=E3=82=82=E3=81=AE=E3=82=92=E5=81=9C=E6=AD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/e2eTest/resources/application.yml | 2 +- src/e2eTest/resources/logback.xml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/e2eTest/resources/application.yml b/src/e2eTest/resources/application.yml index 69e0ebcd..ad806350 100644 --- a/src/e2eTest/resources/application.yml +++ b/src/e2eTest/resources/application.yml @@ -22,7 +22,7 @@ spring: clean-disabled: false datasource: driver-class-name: org.h2.Driver - url: "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=true;TRACE_LEVEL_FILE=4;" + url: "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=true" username: "" password: data: diff --git a/src/e2eTest/resources/logback.xml b/src/e2eTest/resources/logback.xml index 7f19e2eb..1eacbcd5 100644 --- a/src/e2eTest/resources/logback.xml +++ b/src/e2eTest/resources/logback.xml @@ -16,4 +16,6 @@ + + From 5ff06b88f713d5bae6a8beb7ae51140e595d0af6 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:53:22 +0900 Subject: [PATCH 084/116] =?UTF-8?q?test:=20=E5=88=A5=E3=81=AE=E3=82=B9?= =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=97=E3=82=92=E8=A6=81=E6=B1=82=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=83=88=E3=83=BC=E3=82=AF=E3=83=B3=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt | 11 +++++ src/e2eTest/resources/logback.xml | 2 +- .../resources/oauth2/Oauth2LoginTest.feature | 45 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt b/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt index ba524c0d..3dfece24 100644 --- a/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt +++ b/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt @@ -38,6 +38,17 @@ class OAuth2LoginTest { ) } + @Karate.Test + @TestFactory + fun `スコープread_statuses write_statusesを持ったトークンの作成`(): Karate { + return KarateUtil.springBootKarateTest( + "Oauth2LoginTest", + "スコープread:statuses write:statusesを持ったトークンの作成", + javaClass, + port + ) + } + companion object { @JvmStatic @AfterAll diff --git a/src/e2eTest/resources/logback.xml b/src/e2eTest/resources/logback.xml index 1eacbcd5..c21752ee 100644 --- a/src/e2eTest/resources/logback.xml +++ b/src/e2eTest/resources/logback.xml @@ -16,6 +16,6 @@ - + diff --git a/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature b/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature index 582af835..f330c369 100644 --- a/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature +++ b/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature @@ -48,3 +48,48 @@ Feature: OAuth2 Login Test And form field scope = 'write read' When method post Then status 200 + + Scenario: スコープread:statuses write:statusesを持ったトークンの作成 + + * def apps = + """ + { + "client_name": "oauth2-test-client-2", + "redirect_uris": "https://usbharu.dev", + "scopes": "read:statuses write:statuses" + } + """ + + Given path '/api/v1/apps' + And request apps + When method post + Then status 200 + + * def client_id = response.client_id + * def client_secret = response.client_secret + + * def authorizeEndpoint = baseUrl + '/oauth/authorize?response_type=code&redirect_uri=https://usbharu.dev&client_id=' + client_id + '&scope=read:statuses+write:statuses' + + Given driver authorizeEndpoint + And driver.input('#username','test-user') + And driver.input('#password','password') + + When driver.submit().click('body > div > form > button') + Then driver.waitForUrl(authorizeEndpoint + "&continue") + And driver.click('/html/body/div/div[4]/div/form/div[1]/input') + And driver.click('/html/body/div/div[4]/div/form/div[2]/input') + + When driver.submit().click('#submit-consent') + Then driver.waitUntil("location.host == 'usbharu.dev'") + + * def code = script("new URLSearchParams(document.location.search).get('code')") + + Given path '/oauth/token' + And form field client_id = client_id + And form field client_secret = client_secret + And form field redirect_uri = 'https://usbharu.dev' + And form field grant_type = 'authorization_code' + And form field code = code + And form field scope = 'write read' + When method post + Then status 200 From e52064c330110f26ea06b91fc9d1d3ae4d08a28e Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:54:44 +0900 Subject: [PATCH 085/116] =?UTF-8?q?test:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt | 6 ------ src/e2eTest/resources/oauth2/test.feature | 9 --------- 2 files changed, 15 deletions(-) delete mode 100644 src/e2eTest/resources/oauth2/test.feature diff --git a/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt b/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt index 3dfece24..c13bd810 100644 --- a/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt +++ b/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt @@ -21,12 +21,6 @@ class OAuth2LoginTest { @LocalServerPort private var port = "" - @Karate.Test - @TestFactory - fun test(): Karate = - Karate.run("test").scenarioName("invalid").relativeTo(javaClass).systemProperty("karate.port", port) - .karateEnv("dev") - @Karate.Test @TestFactory fun `スコープwrite readを持ったトークンの作成`(): Karate { diff --git a/src/e2eTest/resources/oauth2/test.feature b/src/e2eTest/resources/oauth2/test.feature deleted file mode 100644 index 7a801de8..00000000 --- a/src/e2eTest/resources/oauth2/test.feature +++ /dev/null @@ -1,9 +0,0 @@ -Feature: test - - Background: - * url baseUrl - - Scenario: test - Given path '/api/v1/apps' - When method get - Then status 401 From faff15f5590105926ee6a8839d27d5f311ad0469 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 10:18:27 +0900 Subject: [PATCH 086/116] =?UTF-8?q?test:=20inbox=E3=81=AB=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=82=BB=E3=82=B9=E3=81=97=E3=81=A6=E3=81=8D=E3=81=9F?= =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=82=92=E5=8F=96=E5=BE=97?= =?UTF-8?q?=E3=81=99=E3=82=8Be2e=E3=83=86=E3=82=B9=E3=83=88=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/e2eTest/kotlin/AssertionUtil.kt | 18 +++ src/e2eTest/kotlin/KarateUtil.kt | 16 +++ .../kotlin/federation/InboxCommonTest.kt | 91 +++++++++++++ src/e2eTest/resources/application.yml | 4 +- .../federation/InboxCommonTest.feature | 22 +++ .../InboxxCommonMockServerTest.feature | 126 ++++++++++++++++++ src/e2eTest/resources/karate-config.js | 9 +- 7 files changed, 282 insertions(+), 4 deletions(-) create mode 100644 src/e2eTest/kotlin/AssertionUtil.kt create mode 100644 src/e2eTest/kotlin/federation/InboxCommonTest.kt create mode 100644 src/e2eTest/resources/federation/InboxCommonTest.feature create mode 100644 src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature diff --git a/src/e2eTest/kotlin/AssertionUtil.kt b/src/e2eTest/kotlin/AssertionUtil.kt new file mode 100644 index 00000000..4d559624 --- /dev/null +++ b/src/e2eTest/kotlin/AssertionUtil.kt @@ -0,0 +1,18 @@ +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Users +import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.selectAll + +object AssertionUtil { + + fun assertUserExist(username: String, domain: String): Boolean { + val selectAll = Users.selectAll() + println(selectAll.fetchSize) + + println(selectAll.toList().size) + + selectAll.map { "@${it[Users.name]}@${it[Users.domain]}" }.forEach { println(it) } + + return Users.select { Users.name eq username and (Users.domain eq domain) }.empty().not() + } +} diff --git a/src/e2eTest/kotlin/KarateUtil.kt b/src/e2eTest/kotlin/KarateUtil.kt index e78e8510..c71cd44b 100644 --- a/src/e2eTest/kotlin/KarateUtil.kt +++ b/src/e2eTest/kotlin/KarateUtil.kt @@ -9,4 +9,20 @@ object KarateUtil { .karateEnv("dev") } } + + fun e2eTest(path: String, scenario: String = "", properties: Map, clazz: Class<*>): Karate { + val run = Karate.run(path) + + val karate = if (scenario.isEmpty()) { + run + } else { + run.scenarioName(scenario) + } + + var relativeTo = karate.relativeTo(clazz) + + properties.map { relativeTo = relativeTo.systemProperty(it.key, it.value) } + + return relativeTo.karateEnv("dev") + } } diff --git a/src/e2eTest/kotlin/federation/InboxCommonTest.kt b/src/e2eTest/kotlin/federation/InboxCommonTest.kt new file mode 100644 index 00000000..59bc4e14 --- /dev/null +++ b/src/e2eTest/kotlin/federation/InboxCommonTest.kt @@ -0,0 +1,91 @@ +package federation + +import AssertionUtil +import KarateUtil +import com.intuit.karate.core.MockServer +import com.intuit.karate.junit5.Karate +import dev.usbharu.hideout.SpringApplication +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import org.flywaydb.core.Flyway +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.TestFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.web.server.LocalServerPort +import org.springframework.transaction.annotation.Transactional +import java.net.MalformedURLException +import java.net.URL + +@SpringBootTest( + classes = [SpringApplication::class], + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT +) +@Transactional +class InboxCommonTest { + @LocalServerPort + private var port = "" + + @Karate.Test + @TestFactory + fun `inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く`(): Karate { + return KarateUtil.e2eTest( + "InboxCommonTest", + "inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く", + mapOf( + "karate.port" to port, + "karate.remotePort" to _remotePort + ), + javaClass + ) + } + + companion object { + + lateinit var server: MockServer + lateinit var _remotePort: String + + @JvmStatic + fun assertUserExist(username: String, domain: String) = runBlocking { + val s = try { + val url = URL(domain) + url.host + ":" + url.port.toString().takeIf { it != "-1" }.orEmpty() + } catch (e: MalformedURLException) { + domain + } + + var check = false + + repeat(10) { + delay(1000) + check = AssertionUtil.assertUserExist(username, s) or check + if (check) { + return@repeat + } + } + + assertTrue(check, "User @$username@$s not exist.") + } + + @JvmStatic + fun getRemotePort(): String = _remotePort + + @BeforeAll + @JvmStatic + fun beforeAll(@Autowired flyway: Flyway) { + server = MockServer.feature("classpath:federation/InboxxCommonMockServerTest.feature").http(0).build() + _remotePort = server.port.toString() + + flyway.clean() + flyway.migrate() + } + + @AfterAll + @JvmStatic + fun afterAll() { + server.stop() + } + } +} diff --git a/src/e2eTest/resources/application.yml b/src/e2eTest/resources/application.yml index ad806350..1013de15 100644 --- a/src/e2eTest/resources/application.yml +++ b/src/e2eTest/resources/application.yml @@ -1,6 +1,6 @@ hideout: url: "https://localhost:8080" - use-mongodb: true + use-mongodb: false security: jwt: generate: true @@ -22,7 +22,7 @@ spring: clean-disabled: false datasource: driver-class-name: org.h2.Driver - url: "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=true" + url: "jdbc:h2:./e2e-test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;CASE_INSENSITIVE_IDENTIFIERS=true;TRACE_LEVEL_FILE=4" username: "" password: data: diff --git a/src/e2eTest/resources/federation/InboxCommonTest.feature b/src/e2eTest/resources/federation/InboxCommonTest.feature new file mode 100644 index 00000000..7d102607 --- /dev/null +++ b/src/e2eTest/resources/federation/InboxCommonTest.feature @@ -0,0 +1,22 @@ +Feature: Inbox Common Test + + Background: + * url baseUrl + + Scenario: inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く + + * def inbox = + """ + { "type": "Follow" } + """ + + Given path `/inbox` + And request inbox +# And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target)", signature="a"' + And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' + When method post + Then status 202 + + * def assertInbox = Java.type(`federation.InboxCommonTest`) + + And assertInbox.assertUserExist('test-user',remoteUrl) diff --git a/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature b/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature new file mode 100644 index 00000000..601dcf83 --- /dev/null +++ b/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature @@ -0,0 +1,126 @@ +Feature: InboxCommonMockServer + + Background: + * def assertInbox = Java.type(`federation.InboxCommonTest`) + + Scenario: pathMatches('/users/test-user') && methodIs('get') + * def remoteUrl = 'http://localhost:' + assertInbox.getRemotePort() + * def userUrl = remoteUrl + '/users/test-user' + + * def person = + """ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "toot": "http://joinmastodon.org/ns#", + "featured": { + "@id": "toot:featured", + "@type": "@id" + }, + "featuredTags": { + "@id": "toot:featuredTags", + "@type": "@id" + }, + "alsoKnownAs": { + "@id": "as:alsoKnownAs", + "@type": "@id" + }, + "movedTo": { + "@id": "as:movedTo", + "@type": "@id" + }, + "schema": "http://schema.org#", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value", + "discoverable": "toot:discoverable", + "Device": "toot:Device", + "Ed25519Signature": "toot:Ed25519Signature", + "Ed25519Key": "toot:Ed25519Key", + "Curve25519Key": "toot:Curve25519Key", + "EncryptedMessage": "toot:EncryptedMessage", + "publicKeyBase64": "toot:publicKeyBase64", + "deviceId": "toot:deviceId", + "claim": { + "@type": "@id", + "@id": "toot:claim" + }, + "fingerprintKey": { + "@type": "@id", + "@id": "toot:fingerprintKey" + }, + "identityKey": { + "@type": "@id", + "@id": "toot:identityKey" + }, + "devices": { + "@type": "@id", + "@id": "toot:devices" + }, + "messageFranking": "toot:messageFranking", + "messageType": "toot:messageType", + "cipherText": "toot:cipherText", + "suspended": "toot:suspended", + "focalPoint": { + "@container": "@list", + "@id": "toot:focalPoint" + } + } + ], + "id": #(userUrl), + "type": "Person", + "following": #(userUrl + '/following'), + "followers": "https://mastodon.social/users/Gargron/followers", + "inbox": "https://mastodon.social/users/Gargron/inbox", + "outbox": "https://mastodon.social/users/Gargron/outbox", + "featured": "https://mastodon.social/users/Gargron/collections/featured", + "featuredTags": "https://mastodon.social/users/Gargron/collections/tags", + "preferredUsername": "test-user", + "name": "test-user", + "summary": "\u003cp\u003eFounder, CEO and lead developer \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.social/@Mastodon\" class=\"u-url mention\"\u003e@\u003cspan\u003eMastodon\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e, Germany.\u003c/p\u003e", + "url": "https://mastodon.social/@Gargron", + "manuallyApprovesFollowers": false, + "discoverable": true, + "published": "2016-03-16T00:00:00Z", + "devices": "https://mastodon.social/users/Gargron/collections/devices", + "alsoKnownAs": [ + "https://tooting.ai/users/Gargron" + ], + "publicKey": { + "id": "https://mastodon.social/users/Gargron#main-key", + "owner": "https://mastodon.social/users/Gargron", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXc4vkECU2/CeuSo1wtn\nFoim94Ne1jBMYxTZ9wm2YTdJq1oiZKif06I2fOqDzY/4q/S9uccrE9Bkajv1dnkO\nVm31QjWlhVpSKynVxEWjVBO5Ienue8gND0xvHIuXf87o61poqjEoepvsQFElA5ym\novljWGSA/jpj7ozygUZhCXtaS2W5AD5tnBQUpcO0lhItYPYTjnmzcc4y2NbJV8hz\n2s2G8qKv8fyimE23gY1XrPJg+cRF+g4PqFXujjlJ7MihD9oqtLGxbu7o1cifTn3x\nBfIdPythWu5b4cujNsB3m3awJjVmx+MHQ9SugkSIYXV0Ina77cTNS0M2PYiH1PFR\nTwIDAQAB\n-----END PUBLIC KEY-----\n" + }, + "tag": [], + "attachment": [ + { + "type": "PropertyValue", + "name": "Patreon", + "value": "\u003ca href=\"https://www.patreon.com/mastodon\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003epatreon.com/mastodon\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" + }, + { + "type": "PropertyValue", + "name": "GitHub", + "value": "\u003ca href=\"https://github.com/Gargron\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/Gargron\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" + } + ], + "endpoints": { + "sharedInbox": "https://mastodon.social/inbox" + }, + "icon": { + "type": "Image", + "mediaType": "image/jpeg", + "url": "https://files.mastodon.social/accounts/avatars/000/000/001/original/dc4286ceb8fab734.jpg" + }, + "image": { + "type": "Image", + "mediaType": "image/jpeg", + "url": "https://files.mastodon.social/accounts/headers/000/000/001/original/3b91c9965d00888b.jpeg" + } +} + + """ + + * def response = person diff --git a/src/e2eTest/resources/karate-config.js b/src/e2eTest/resources/karate-config.js index 9fd5dd1e..a83c2bb4 100644 --- a/src/e2eTest/resources/karate-config.js +++ b/src/e2eTest/resources/karate-config.js @@ -7,18 +7,23 @@ function fn() { } let config; if (env === 'test') { + let remotePort = karate.properties['karate.remotePort'] || '8081' config = { - baseUrl: 'https://test-hideout.usbharu.dev' + baseUrl: 'https://test-hideout.usbharu.dev', + remoteUrl: 'http://localhost:' + remotePort } } else if (env === 'dev') { let port = karate.properties['karate.port'] || '8080' + let remotePort = karate.properties['karate.remotePort'] || '8081' config = { - baseUrl: 'http://localhost:' + port + baseUrl: 'http://localhost:' + port, + remoteUrl: 'http://localhost:' + remotePort } } else { throw 'Unknown environment [' + env + '].' } // don't waste time waiting for a connection or if servers don't respond within 0,3 seconds + karate.configure('connectTimeout', 1000); karate.configure('readTimeout', 1000); return config; From bfc5c9e1106bb677c3886c85d00213633e27489a Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 10:26:48 +0900 Subject: [PATCH 087/116] =?UTF-8?q?fix:=20=E4=B8=80=E9=83=A8=E3=81=AEAP=20?= =?UTF-8?q?Object=E3=81=8C=E3=83=87=E3=82=B7=E3=83=AA=E3=82=A2=E3=83=A9?= =?UTF-8?q?=E3=82=A4=E3=82=BA=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=8F=E3=81=AA?= =?UTF-8?q?=E3=81=A3=E3=81=A6=E3=81=84=E3=81=9F=E5=95=8F=E9=A1=8C=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/activitypub/domain/model/Key.kt | 3 +- .../activitypub/domain/model/Person.kt | 2 +- .../service/objects/user/APUserService.kt | 2 - .../application/config/ActivityPubConfig.kt | 7 +- .../httpsignature/HttpRequestMixIn.kt | 33 ++++++++ .../domain/model/KeySerializeTest.kt | 24 ++++++ .../domain/model/PersonSerializeTest.kt | 75 +++++++++++++++++++ .../objects/note/APNoteServiceImplTest.kt | 2 - 8 files changed, 139 insertions(+), 9 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt create mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/KeySerializeTest.kt create mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/PersonSerializeTest.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt index 7e22097c..e821601f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt @@ -3,12 +3,11 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object open class Key( - type: List, override val id: String, val owner: String, val publicKeyPem: String ) : Object( - type = add(list = type, type = "Key") + type = add(list = emptyList(), type = "Key") ), HasId { diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt index fe1b2d5f..4791fa68 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt @@ -14,7 +14,7 @@ constructor( var outbox: String, var url: String, private var icon: Image?, - var publicKey: Key?, + var publicKey: Key, var endpoints: Map = emptyMap(), var followers: String?, var following: String? diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt index 143df20b..9556590f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt @@ -61,7 +61,6 @@ class APUserServiceImpl( url = "$userUrl/icon.png" ), publicKey = Key( - type = emptyList(), id = userEntity.keyId, owner = userUrl, publicKeyPem = userEntity.publicKey @@ -129,7 +128,6 @@ class APUserServiceImpl( url = "$id/icon.png" ), publicKey = Key( - type = emptyList(), id = userEntity.keyId, owner = id, publicKeyPem = userEntity.publicKey diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt index d661a30a..c99d07b2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt @@ -7,6 +7,8 @@ import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import dev.usbharu.hideout.core.infrastructure.httpsignature.HttpRequestMixIn +import dev.usbharu.httpsignature.common.HttpRequest import dev.usbharu.httpsignature.sign.HttpSignatureSigner import dev.usbharu.httpsignature.sign.RsaSha256HttpSignatureSigner import org.springframework.beans.factory.annotation.Qualifier @@ -24,12 +26,13 @@ class ActivityPubConfig { val objectMapper = jacksonObjectMapper() .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) - .setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY)) - .setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY)) + .setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) + .setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP)) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .configure(JsonParser.Feature.ALLOW_COMMENTS, true) .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) .configure(JsonParser.Feature.ALLOW_TRAILING_COMMA, true) + .addMixIn(HttpRequest::class.java, HttpRequestMixIn::class.java) return objectMapper } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt new file mode 100644 index 00000000..3686d3c9 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt @@ -0,0 +1,33 @@ +package dev.usbharu.hideout.core.infrastructure.httpsignature + +import com.fasterxml.jackson.annotation.JsonSubTypes +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import dev.usbharu.httpsignature.common.HttpHeaders +import dev.usbharu.httpsignature.common.HttpMethod +import dev.usbharu.httpsignature.common.HttpRequest +import java.net.URL + + +@JsonDeserialize(using = HttpRequestDeserializer::class) +@JsonSubTypes +abstract class HttpRequestMixIn + +class HttpRequestDeserializer : JsonDeserializer() { + override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): HttpRequest { + + val readTree: JsonNode = p.codec.readTree(p) + + + + return HttpRequest( + URL(readTree["url"].textValue()), + HttpHeaders(emptyMap()), + HttpMethod.valueOf(readTree["method"].textValue()) + ) + } + +} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/KeySerializeTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/KeySerializeTest.kt new file mode 100644 index 00000000..d6e1be7a --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/KeySerializeTest.kt @@ -0,0 +1,24 @@ +package dev.usbharu.hideout.activitypub.domain.model + +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.application.config.ActivityPubConfig +import org.junit.jupiter.api.Test + +class KeySerializeTest { + @Test + fun Keyのデシリアライズができる() { + //language=JSON + val trimIndent = """ + { + "id": "https://mastodon.social/users/Gargron#main-key", + "owner": "https://mastodon.social/users/Gargron", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXc4vkECU2/CeuSo1wtn\nFoim94Ne1jBMYxTZ9wm2YTdJq1oiZKif06I2fOqDzY/4q/S9uccrE9Bkajv1dnkO\nVm31QjWlhVpSKynVxEWjVBO5Ienue8gND0xvHIuXf87o61poqjEoepvsQFElA5ym\novljWGSA/jpj7ozygUZhCXtaS2W5AD5tnBQUpcO0lhItYPYTjnmzcc4y2NbJV8hz\n2s2G8qKv8fyimE23gY1XrPJg+cRF+g4PqFXujjlJ7MihD9oqtLGxbu7o1cifTn3x\nBfIdPythWu5b4cujNsB3m3awJjVmx+MHQ9SugkSIYXV0Ina77cTNS0M2PYiH1PFR\nTwIDAQAB\n-----END PUBLIC KEY-----\n" + } + """.trimIndent() + + val objectMapper = ActivityPubConfig().objectMapper() + + val readValue = objectMapper.readValue(trimIndent) + + } +} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/PersonSerializeTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/PersonSerializeTest.kt new file mode 100644 index 00000000..9b344337 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/PersonSerializeTest.kt @@ -0,0 +1,75 @@ +package dev.usbharu.hideout.activitypub.domain.model + +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.application.config.ActivityPubConfig +import org.junit.jupiter.api.Test + +class PersonSerializeTest { + @Test + fun MastodonのPersonのデシリアライズができる() { + val personString = """ + { + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1" + ], + "id": "https://mastodon.social/users/Gargron", + "type": "Person", + "following": "https://mastodon.social/users/Gargron/following", + "followers": "https://mastodon.social/users/Gargron/followers", + "inbox": "https://mastodon.social/users/Gargron/inbox", + "outbox": "https://mastodon.social/users/Gargron/outbox", + "featured": "https://mastodon.social/users/Gargron/collections/featured", + "featuredTags": "https://mastodon.social/users/Gargron/collections/tags", + "preferredUsername": "Gargron", + "name": "Eugen Rochko", + "summary": "\u003cp\u003eFounder, CEO and lead developer \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.social/@Mastodon\" class=\"u-url mention\"\u003e@\u003cspan\u003eMastodon\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e, Germany.\u003c/p\u003e", + "url": "https://mastodon.social/@Gargron", + "manuallyApprovesFollowers": false, + "discoverable": true, + "published": "2016-03-16T00:00:00Z", + "devices": "https://mastodon.social/users/Gargron/collections/devices", + "alsoKnownAs": [ + "https://tooting.ai/users/Gargron" + ], + "publicKey": { + "id": "https://mastodon.social/users/Gargron#main-key", + "owner": "https://mastodon.social/users/Gargron", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXc4vkECU2/CeuSo1wtn\nFoim94Ne1jBMYxTZ9wm2YTdJq1oiZKif06I2fOqDzY/4q/S9uccrE9Bkajv1dnkO\nVm31QjWlhVpSKynVxEWjVBO5Ienue8gND0xvHIuXf87o61poqjEoepvsQFElA5ym\novljWGSA/jpj7ozygUZhCXtaS2W5AD5tnBQUpcO0lhItYPYTjnmzcc4y2NbJV8hz\n2s2G8qKv8fyimE23gY1XrPJg+cRF+g4PqFXujjlJ7MihD9oqtLGxbu7o1cifTn3x\nBfIdPythWu5b4cujNsB3m3awJjVmx+MHQ9SugkSIYXV0Ina77cTNS0M2PYiH1PFR\nTwIDAQAB\n-----END PUBLIC KEY-----\n" + }, + "tag": [], + "attachment": [ + { + "type": "PropertyValue", + "name": "Patreon", + "value": "\u003ca href=\"https://www.patreon.com/mastodon\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003epatreon.com/mastodon\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" + }, + { + "type": "PropertyValue", + "name": "GitHub", + "value": "\u003ca href=\"https://github.com/Gargron\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/Gargron\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" + } + ], + "endpoints": { + "sharedInbox": "https://mastodon.social/inbox" + }, + "icon": { + "type": "Image", + "mediaType": "image/jpeg", + "url": "https://files.mastodon.social/accounts/avatars/000/000/001/original/dc4286ceb8fab734.jpg" + }, + "image": { + "type": "Image", + "mediaType": "image/jpeg", + "url": "https://files.mastodon.social/accounts/headers/000/000/001/original/3b91c9965d00888b.jpeg" + } + } + + """.trimIndent() + + + val objectMapper = ActivityPubConfig().objectMapper() + + val readValue = objectMapper.readValue(personString) + } +} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt index 5ed6597d..b8713816 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt @@ -126,7 +126,6 @@ class APNoteServiceImplTest { url = user.url + "/icon.png" ), publicKey = Key( - type = emptyList(), id = user.keyId, owner = user.url, publicKeyPem = user.publicKey @@ -245,7 +244,6 @@ class APNoteServiceImplTest { url = user.url + "/icon.png" ), publicKey = Key( - type = emptyList(), id = user.keyId, owner = user.url, publicKeyPem = user.publicKey From be7bd590eb76d03c52f3d07a29e0e70e5a7114f3 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 10:27:46 +0900 Subject: [PATCH 088/116] =?UTF-8?q?fix:=20HTTP=20Signature=E3=81=AE?= =?UTF-8?q?=E3=83=98=E3=83=83=E3=83=80=E3=83=BC=E5=8F=96=E5=BE=97=E6=99=82?= =?UTF-8?q?=E3=81=AB=E5=A4=A7=E6=96=87=E5=AD=97=E5=B0=8F=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=81=AE=E5=B7=AE=E3=82=92=E7=84=A1=E8=A6=96=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/interfaces/api/inbox/InboxControllerImpl.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt index d87d045a..9fb8d101 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt @@ -23,7 +23,9 @@ class InboxControllerImpl(private val apService: APService) : InboxController { val request = (requireNotNull(RequestContextHolder.getRequestAttributes()) as ServletRequestAttributes).request val headersList = request.headerNames?.toList().orEmpty() - if (headersList.contains("Signature").not()) { + LOGGER.trace("Inbox Headers {}", headersList) + + if (headersList.map { it.lowercase() }.contains("signature").not()) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED) .header( WWW_AUTHENTICATE, From 158cd3a6dfea55f9c9484fc2f32b5a1577d59b1c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 10:28:19 +0900 Subject: [PATCH 089/116] =?UTF-8?q?fix:=20Inbox=E5=87=A6=E7=90=86=E3=81=AE?= =?UTF-8?q?=E3=83=88=E3=83=A9=E3=83=B3=E3=82=B6=E3=82=AF=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/inbox/InboxJobProcessor.kt | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt index 1ca31144..91c3f4ed 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt @@ -34,25 +34,37 @@ class InboxJobProcessor( private val transaction: Transaction ) : JobProcessor { - private suspend fun verifyHttpSignature(httpRequest: HttpRequest, signature: Signature): Boolean { + private suspend fun verifyHttpSignature( + httpRequest: HttpRequest, + signature: Signature, + transaction: Transaction + ): Boolean { val requiredHeaders = when (httpRequest.method) { HttpMethod.GET -> getRequiredHeaders HttpMethod.POST -> postRequiredHeaders } if (signature.headers.containsAll(requiredHeaders).not()) { + logger.warn("FAILED Invalid signature. require: {}", requiredHeaders) return false } - val user = try { - userQueryService.findByKeyId(signature.keyId) - } catch (_: FailedToGetResourcesException) { - apUserService.fetchPersonWithEntity(signature.keyId).second + val user = transaction.transaction { + try { + userQueryService.findByKeyId(signature.keyId) + } catch (_: FailedToGetResourcesException) { + apUserService.fetchPersonWithEntity(signature.keyId).second + } } - val verify = signatureVerifier.verify( - httpRequest, - PublicKey(RsaUtil.decodeRsaPublicKeyPem(user.publicKey), signature.keyId) - ) + val verify = try { + signatureVerifier.verify( + httpRequest, + PublicKey(RsaUtil.decodeRsaPublicKeyPem(user.publicKey), signature.keyId) + ) + } catch (e: Exception) { + logger.warn("FAILED Verify Http Signature", e) + return false + } return verify.success } @@ -60,6 +72,7 @@ class InboxJobProcessor( @Suppress("TooGenericExceptionCaught") private fun parseSignatureHeader(httpHeaders: HttpHeaders): Signature? { return try { + println("Signature Header =" + httpHeaders.get("Signature").single()) signatureHeaderParser.parse(httpHeaders) } catch (e: RuntimeException) { logger.trace("FAILED parse signature header", e) @@ -67,7 +80,8 @@ class InboxJobProcessor( } } - override suspend fun process(param: InboxJobParam) = transaction.transaction { + override suspend fun process(param: InboxJobParam) { + val jsonNode = objectMapper.readTree(param.json) logger.info("START Process inbox. type: {}", param.type) @@ -83,22 +97,24 @@ class InboxJobProcessor( logger.debug("Has signature? {}", signature != null) - val verify = signature?.let { verifyHttpSignature(httpRequest, it) } ?: false + val verify = signature?.let { verifyHttpSignature(httpRequest, it, transaction) } ?: false - logger.debug("Is verifying success? {}", verify) + transaction.transaction { + logger.debug("Is verifying success? {}", verify) - val activityPubProcessor = - activityPubProcessorList.firstOrNull { it.isSupported(param.type) } as ActivityPubProcessor? + val activityPubProcessor = + activityPubProcessorList.firstOrNull { it.isSupported(param.type) } as ActivityPubProcessor? - if (activityPubProcessor == null) { - logger.warn("ActivityType {} is not support.", param.type) - throw IllegalStateException("ActivityPubProcessor not found.") + if (activityPubProcessor == null) { + logger.warn("ActivityType {} is not support.", param.type) + throw IllegalStateException("ActivityPubProcessor not found.") + } + + val value = objectMapper.treeToValue(jsonNode, activityPubProcessor.type()) + activityPubProcessor.process(ActivityPubProcessContext(value, jsonNode, httpRequest, signature, verify)) + + logger.info("SUCCESS Process inbox. type: {}", param.type) } - - val value = objectMapper.treeToValue(jsonNode, activityPubProcessor.type()) - activityPubProcessor.process(ActivityPubProcessContext(value, jsonNode, httpRequest, signature, verify)) - - logger.info("SUCCESS Process inbox. type: {}", param.type) } override fun job(): InboxJob = InboxJob From 0d5fecbd4d8d852f32b7ee4ef55e3923071c20f4 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 10:28:44 +0900 Subject: [PATCH 090/116] =?UTF-8?q?fix:=20=E3=82=B8=E3=83=A7=E3=83=96?= =?UTF-8?q?=E3=82=AD=E3=83=A5=E3=83=BC=E3=81=AE=E4=BF=AE=E6=AD=A3=20?= =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kjobexposed/KJobJobQueueParentService.kt | 3 +++ .../kjobmongodb/KJobMongoJobQueueWorkerService.kt | 14 +++++++++++++- .../kjobmongodb/KjobMongoJobQueueParentService.kt | 9 +++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueParentService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueParentService.kt index aadf0f9f..f61949e2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueParentService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueParentService.kt @@ -32,7 +32,10 @@ class KJobJobQueueParentService : JobQueueParentService { } override suspend fun > scheduleTypeSafe(job: J, jobProps: T) { + logger.debug("SCHEDULE Job: {}", job.name) + logger.trace("Job props: {}", jobProps) val convert: ScheduleContext.(J) -> Unit = job.convert(jobProps) kjob.schedule(job, convert) + logger.debug("SUCCESS Schedule Job: {}", job.name) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt index bb48b08b..fd57f210 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt @@ -2,6 +2,7 @@ package dev.usbharu.hideout.core.infrastructure.kjobmongodb import com.mongodb.reactivestreams.client.MongoClient import dev.usbharu.hideout.core.external.job.HideoutJob +import dev.usbharu.hideout.core.service.job.JobProcessor import dev.usbharu.hideout.core.service.job.JobQueueWorkerService import kjob.core.dsl.JobContextWithProps import kjob.core.dsl.JobRegisterContext @@ -13,7 +14,10 @@ import org.springframework.stereotype.Service @Service @ConditionalOnProperty(name = ["hideout.use-mongodb"], havingValue = "true", matchIfMissing = false) -class KJobMongoJobQueueWorkerService(private val mongoClient: MongoClient) : JobQueueWorkerService, AutoCloseable { +class KJobMongoJobQueueWorkerService( + private val mongoClient: MongoClient, + private val jobQueueProcessorList: List> +) : JobQueueWorkerService, AutoCloseable { val kjob by lazy { kjob(Mongo) { client = mongoClient @@ -30,6 +34,14 @@ class KJobMongoJobQueueWorkerService(private val mongoClient: MongoClient) : Job defines.forEach { job -> kjob.register(job.first, job.second) } + for (jobProcessor in jobQueueProcessorList) { + kjob.register(jobProcessor.job()) { + execute { + val param = it.convertUnsafe(props) + jobProcessor.process(param) + } + } + } } override fun close() { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KjobMongoJobQueueParentService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KjobMongoJobQueueParentService.kt index f66f58ef..37f600dc 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KjobMongoJobQueueParentService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KjobMongoJobQueueParentService.kt @@ -7,6 +7,7 @@ import kjob.core.Job import kjob.core.dsl.ScheduleContext import kjob.core.kjob import kjob.mongo.Mongo +import org.slf4j.LoggerFactory import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.stereotype.Service @@ -26,15 +27,23 @@ class KjobMongoJobQueueParentService(private val mongoClient: MongoClient) : Job @Deprecated("use type safe → scheduleTypeSafe") override suspend fun schedule(job: J, block: ScheduleContext.(J) -> Unit) { + logger.debug("SCHEDULE Job: {}", job.name) kjob.schedule(job, block) } override suspend fun > scheduleTypeSafe(job: J, jobProps: T) { + logger.debug("SCHEDULE Job: {}", job.name) + logger.trace("Job props: {}", jobProps) val convert = job.convert(jobProps) kjob.schedule(job, convert) + logger.debug("SUCCESS Job: {}", job.name) } override fun close() { kjob.shutdown() } + + companion object { + private val logger = LoggerFactory.getLogger(KjobMongoJobQueueParentService::class.java) + } } From 51aeff6015062bc5884683f4fa5b98b5c0e2cdae Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 10:29:22 +0900 Subject: [PATCH 091/116] =?UTF-8?q?fix:=20=E3=83=88=E3=83=A9=E3=83=B3?= =?UTF-8?q?=E3=82=B6=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/activitypub/service/objects/user/APUserService.kt | 2 ++ .../dev/usbharu/hideout/core/service/user/UserServiceImpl.kt | 2 ++ .../interfaces/api/actor/UserAPControllerImplTest.kt | 3 +-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt index 9556590f..47568b8d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt @@ -14,6 +14,7 @@ import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.user.RemoteUserCreateDto import dev.usbharu.hideout.core.service.user.UserService import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional interface APUserService { suspend fun getPersonByName(name: String): Person @@ -74,6 +75,7 @@ class APUserServiceImpl( override suspend fun fetchPerson(url: String, targetActor: String?): Person = fetchPersonWithEntity(url, targetActor).first + @Transactional override suspend fun fetchPersonWithEntity(url: String, targetActor: String?): Pair { return try { val userEntity = userQueryService.findByUrl(url) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index fa1c0f97..94346400 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -12,6 +12,7 @@ import dev.usbharu.hideout.core.service.instance.InstanceService import org.jetbrains.exposed.exceptions.ExposedSQLException import org.slf4j.LoggerFactory import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import java.time.Instant @Service @@ -57,6 +58,7 @@ class UserServiceImpl( return userRepository.save(userEntity) } + @Transactional override suspend fun createRemoteUser(user: RemoteUserCreateDto): User { @Suppress("TooGenericExceptionCaught") val instance = try { diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImplTest.kt index 42f44e27..5d7bab4d 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImplTest.kt @@ -55,8 +55,7 @@ class UserAPControllerImplTest { publicKey = Key( id = "https://example.com/users/hoge#pubkey", owner = "https://example.com/users/hoge", - publicKeyPem = "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", - type = emptyList() + publicKeyPem = "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----" ), endpoints = mapOf("sharedInbox" to "https://example.com/inbox"), followers = "https://example.com/users/hoge/followers", From 14034cd1b9d3359ad46e1adae320e027c791afa3 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 11:10:59 +0900 Subject: [PATCH 092/116] =?UTF-8?q?test:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E7=94=A8=E3=81=AE=E3=83=80=E3=83=9F=E3=83=BC=E3=83=87=E3=83=BC?= =?UTF-8?q?=E3=82=BF=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InboxxCommonMockServerTest.feature | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature b/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature index 601dcf83..9457b36e 100644 --- a/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature +++ b/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature @@ -72,52 +72,52 @@ Feature: InboxCommonMockServer "id": #(userUrl), "type": "Person", "following": #(userUrl + '/following'), - "followers": "https://mastodon.social/users/Gargron/followers", - "inbox": "https://mastodon.social/users/Gargron/inbox", - "outbox": "https://mastodon.social/users/Gargron/outbox", - "featured": "https://mastodon.social/users/Gargron/collections/featured", - "featuredTags": "https://mastodon.social/users/Gargron/collections/tags", + "followers": #(userUrl + '/followers'), + "inbox": #(userUrl + '/inbox'), + "outbox": #(userUrl + '/outbox'), + "featured": #(userUrl + '/collections/featured'), + "featuredTags": #(userUrl + '/collections/tags'), "preferredUsername": "test-user", "name": "test-user", - "summary": "\u003cp\u003eFounder, CEO and lead developer \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.social/@Mastodon\" class=\"u-url mention\"\u003e@\u003cspan\u003eMastodon\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e, Germany.\u003c/p\u003e", - "url": "https://mastodon.social/@Gargron", + "summary": "E2E Test User Jaga/Cotlin/Winter Boot/Ktol\nYonTude: https://example.com\nY(Tvvitter): https://example.com\n", + "url": #(userUrl + '/@test-user'), "manuallyApprovesFollowers": false, "discoverable": true, "published": "2016-03-16T00:00:00Z", - "devices": "https://mastodon.social/users/Gargron/collections/devices", + "devices": #(userUrl + '/collections/devices'), "alsoKnownAs": [ - "https://tooting.ai/users/Gargron" + "https://example.com/users/test-users" ], "publicKey": { - "id": "https://mastodon.social/users/Gargron#main-key", - "owner": "https://mastodon.social/users/Gargron", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXc4vkECU2/CeuSo1wtn\nFoim94Ne1jBMYxTZ9wm2YTdJq1oiZKif06I2fOqDzY/4q/S9uccrE9Bkajv1dnkO\nVm31QjWlhVpSKynVxEWjVBO5Ienue8gND0xvHIuXf87o61poqjEoepvsQFElA5ym\novljWGSA/jpj7ozygUZhCXtaS2W5AD5tnBQUpcO0lhItYPYTjnmzcc4y2NbJV8hz\n2s2G8qKv8fyimE23gY1XrPJg+cRF+g4PqFXujjlJ7MihD9oqtLGxbu7o1cifTn3x\nBfIdPythWu5b4cujNsB3m3awJjVmx+MHQ9SugkSIYXV0Ina77cTNS0M2PYiH1PFR\nTwIDAQAB\n-----END PUBLIC KEY-----\n" + "id": #(userUrl + '#main-key'), + "owner": #(userUrl), + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvmtKo0xYGXR4M0LQQhK4\nBkKpRvvUxrqGV6Ew4CBHSyzdnbFsiBqHUWz4JvRiQvAPqQ4jQFpxVZCPr9xx6lJp\nx7EAAKIdVTnBBV4CYfu7QPsRqtjbB5408q+mo5oUXNs8xg2tcC42p2SJ5CRJX/fr\nOgCZwo3LC9pOBdCQZ+tiiPmWNBTNby99JZn4D/xNcwuhV04qcPoHYD9OPuxxGyzc\naVJ2mqJmvi/lewQuR8qnUIbz+Gik+xvyG6LmyuDoa1H2LDQfQXYb62G70HsYdu7a\ndObvZovytp+kkjP/cUaIYkhhOAYqAA4zCwVRY4NHK0MAMq9sMoUfNJa8U+zR9NvD\noQIDAQAB\n-----END PUBLIC KEY-----\n" }, "tag": [], "attachment": [ { "type": "PropertyValue", - "name": "Patreon", - "value": "\u003ca href=\"https://www.patreon.com/mastodon\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003epatreon.com/mastodon\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" + "name": "Pixib Fan-Bridge", + "value": "\u003ca href=\"https://example.com/hideout\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003eexample.com/hideout\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" }, { "type": "PropertyValue", "name": "GitHub", - "value": "\u003ca href=\"https://github.com/Gargron\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/Gargron\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" + "value": "\u003ca href=\"https://github.com/usbharu/hideout\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/usbharu/hideout\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" } ], "endpoints": { - "sharedInbox": "https://mastodon.social/inbox" + "sharedInbox": #(userUrl + 'inbox') }, "icon": { "type": "Image", "mediaType": "image/jpeg", - "url": "https://files.mastodon.social/accounts/avatars/000/000/001/original/dc4286ceb8fab734.jpg" + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/Destroyer_Vozbuzhdenyy.jpg/320px-Destroyer_Vozbuzhdenyy.jpg" }, "image": { "type": "Image", "mediaType": "image/jpeg", - "url": "https://files.mastodon.social/accounts/headers/000/000/001/original/3b91c9965d00888b.jpeg" + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/63/Views_of_Mount_Fuji_from_%C5%8Cwakudani_20211202.jpg/320px-Views_of_Mount_Fuji_from_%C5%8Cwakudani_20211202.jpg" } } From 5a77b9e6691b5d67eba85169465f757eeb834890 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 13:02:54 +0900 Subject: [PATCH 093/116] =?UTF-8?q?test:=20user-inbox=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/federation/InboxCommonTest.kt | 29 +++++++- .../federation/InboxCommonTest.feature | 71 +++++++++++++++++++ .../InboxxCommonMockServerTest.feature | 24 +++++-- .../core/service/instance/InstanceService.kt | 2 +- .../core/service/user/UserServiceImpl.kt | 6 +- 5 files changed, 121 insertions(+), 11 deletions(-) diff --git a/src/e2eTest/kotlin/federation/InboxCommonTest.kt b/src/e2eTest/kotlin/federation/InboxCommonTest.kt index 59bc4e14..dff26b42 100644 --- a/src/e2eTest/kotlin/federation/InboxCommonTest.kt +++ b/src/e2eTest/kotlin/federation/InboxCommonTest.kt @@ -42,9 +42,35 @@ class InboxCommonTest { ) } - companion object { + @Karate.Test + @TestFactory + fun `user-inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く`(): Karate { + return KarateUtil.e2eTest( + "InboxCommonTest", + "user-inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く", + mapOf( + "karate.port" to port, + "karate.remotePort" to _remotePort + ), + javaClass + ) + } + @Karate.Test + @TestFactory + fun `inboxにHTTP Signatureがないリクエストがきたら401を返す`(): Karate { + return KarateUtil.e2eTest( + "InboxCommonTest", + "inboxにHTTP Signatureがないリクエストがきたら401を返す", + mapOf("karate.port" to port), + javaClass + ) + } + + + companion object { lateinit var server: MockServer + lateinit var _remotePort: String @JvmStatic @@ -81,7 +107,6 @@ class InboxCommonTest { flyway.clean() flyway.migrate() } - @AfterAll @JvmStatic fun afterAll() { diff --git a/src/e2eTest/resources/federation/InboxCommonTest.feature b/src/e2eTest/resources/federation/InboxCommonTest.feature index 7d102607..142f6746 100644 --- a/src/e2eTest/resources/federation/InboxCommonTest.feature +++ b/src/e2eTest/resources/federation/InboxCommonTest.feature @@ -5,6 +5,14 @@ Feature: Inbox Common Test Scenario: inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く + * url remoteUrl + + Given path '/internal-assertion-api/requests/deleteAll' + When method post + Then status 200 + + * url baseUrl + * def inbox = """ { "type": "Follow" } @@ -20,3 +28,66 @@ Feature: Inbox Common Test * def assertInbox = Java.type(`federation.InboxCommonTest`) And assertInbox.assertUserExist('test-user',remoteUrl) + + * url remoteUrl + + Given path '/internal-assertion-api/requests' + When method get + Then status 200 + + * url baseUrl + + * print response + Then match response.req == ['/users/test-user'] + + + Scenario: inboxにHTTP Signatureがないリクエストがきたら401を返す + + * def inbox = + """ + {"type": "Follow"} + """ + + Given path '/inbox' + And request inbox + When method post + Then status 401 + + + Scenario: user-inboxにHTTP Signature付きのリクエストがあったらリモートに取得しに行く + + * url remoteUrl + + Given path '/internal-assertion-api/requests/deleteAll' + When method post + Then status 200 + + * url baseUrl + + * def inbox = + """ + { "type": "Follow" } + """ + + Given path `/inbox` + And request inbox +# And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target)", signature="a"' + And header Signature = 'keyId="'+ remoteUrl +'/users/test-user2#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' + When method post + Then status 202 + + * def assertInbox = Java.type(`federation.InboxCommonTest`) + + And assertInbox.assertUserExist('test-user2',remoteUrl) + + + * url remoteUrl + + Given path '/internal-assertion-api/requests' + When method get + Then status 200 + + * url baseUrl + + * print response + Then match response.req == ['/users/test-user2'] diff --git a/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature b/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature index 9457b36e..519b7d05 100644 --- a/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature +++ b/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature @@ -2,10 +2,13 @@ Feature: InboxCommonMockServer Background: * def assertInbox = Java.type(`federation.InboxCommonTest`) + * def req = {req: []} - Scenario: pathMatches('/users/test-user') && methodIs('get') + Scenario: pathMatches('/users/{username}') && methodIs('get') * def remoteUrl = 'http://localhost:' + assertInbox.getRemotePort() - * def userUrl = remoteUrl + '/users/test-user' + * def username = pathParams.username + * def userUrl = remoteUrl + '/users/' + username + * def person = """ @@ -77,16 +80,16 @@ Feature: InboxCommonMockServer "outbox": #(userUrl + '/outbox'), "featured": #(userUrl + '/collections/featured'), "featuredTags": #(userUrl + '/collections/tags'), - "preferredUsername": "test-user", - "name": "test-user", + "preferredUsername": #(username), + "name": #(username), "summary": "E2E Test User Jaga/Cotlin/Winter Boot/Ktol\nYonTude: https://example.com\nY(Tvvitter): https://example.com\n", - "url": #(userUrl + '/@test-user'), + "url": #(userUrl + '/@' + username), "manuallyApprovesFollowers": false, "discoverable": true, "published": "2016-03-16T00:00:00Z", "devices": #(userUrl + '/collections/devices'), "alsoKnownAs": [ - "https://example.com/users/test-users" + #( 'https://example.com/users/' + username) ], "publicKey": { "id": #(userUrl + '#main-key'), @@ -122,5 +125,12 @@ Feature: InboxCommonMockServer } """ - + * set req.req[] = '/users/' + username * def response = person + + Scenario: pathMatches('/internal-assertion-api/requests') && methodIs('get') + * def response = req + + Scenario: pathMatches('/internal-assertion-api/requests/deleteAll') && methodIs('post') + * set req.req = [] + * def responseStatus = 200 diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt index 2c773ce0..d06e53ad 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt @@ -81,7 +81,7 @@ class InstanceServiceImpl( } else -> { - TODO() + throw IllegalStateException("Unknown nodeinfo versions: $key url: $value") } } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index 94346400..d2a2e45e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -60,6 +60,7 @@ class UserServiceImpl( @Transactional override suspend fun createRemoteUser(user: RemoteUserCreateDto): User { + logger.info("START Create New remote user. name: {} url: {}", user.name, user.url) @Suppress("TooGenericExceptionCaught") val instance = try { instanceService.fetchInstance(user.url, user.sharedInbox) @@ -86,8 +87,11 @@ class UserServiceImpl( instance = instance?.id ) return try { - userRepository.save(userEntity) + val save = userRepository.save(userEntity) + logger.warn("SUCCESS Create New remote user. id: {} name: {} url: {}", userEntity.id, user.name, user.url) + save } catch (_: ExposedSQLException) { + logger.warn("FAILED User already exists. name: {} url: {}", user.name, user.url) userQueryService.findByUrl(user.url) } } From e1d99b7ea9dc4b0ed28b4dafc6468f43f417d584 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 13:03:45 +0900 Subject: [PATCH 094/116] =?UTF-8?q?fix:=20#184=20ResourceResolver=E3=81=A7?= =?UTF-8?q?=E5=88=A9=E7=94=A8=E3=81=97=E3=81=A6=E3=81=84=E3=82=8BCacheMana?= =?UTF-8?q?ger=E3=81=A7=E4=BE=8B=E5=A4=96=E3=81=8C=E7=99=BA=E7=94=9F?= =?UTF-8?q?=E3=81=97=E3=81=9F=E5=A0=B4=E5=90=88=E3=81=AB=E3=83=AD=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=81=8C=E8=A7=A3=E6=94=BE=E3=81=95=E3=82=8C=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=83=90=E3=82=B0=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/core/service/resource/InMemoryCacheManager.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt index 587829c9..d5ca028d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt @@ -29,7 +29,12 @@ class InMemoryCacheManager : CacheManager { } } if (needRunBlock) { - val processed = block() + val processed = try { + block() + } catch (e: Exception) { + cacheKey.remove(key) + throw e + } if (cacheKey.containsKey(key)) { valueStore[key] = processed From beeb27350ec9102be27b60c853b62c748ca21e58 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 13:09:21 +0900 Subject: [PATCH 095/116] =?UTF-8?q?test:=20user-inbox=E3=81=AE=E8=AA=8D?= =?UTF-8?q?=E8=A8=BC=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/e2eTest/kotlin/federation/InboxCommonTest.kt | 10 ++++++++++ .../resources/federation/InboxCommonTest.feature | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/e2eTest/kotlin/federation/InboxCommonTest.kt b/src/e2eTest/kotlin/federation/InboxCommonTest.kt index dff26b42..43c204e8 100644 --- a/src/e2eTest/kotlin/federation/InboxCommonTest.kt +++ b/src/e2eTest/kotlin/federation/InboxCommonTest.kt @@ -67,6 +67,16 @@ class InboxCommonTest { ) } + @Karate.Test + @TestFactory + fun `user-inboxにHTTP Signatureがないリクエストがきたら401を返す`(): Karate { + return KarateUtil.e2eTest( + "InboxCommonTest", + "user-inboxにHTTP Signatureがないリクエストがきたら401を返す", + mapOf("karate.port" to port), + javaClass + ) + } companion object { lateinit var server: MockServer diff --git a/src/e2eTest/resources/federation/InboxCommonTest.feature b/src/e2eTest/resources/federation/InboxCommonTest.feature index 142f6746..eec903d3 100644 --- a/src/e2eTest/resources/federation/InboxCommonTest.feature +++ b/src/e2eTest/resources/federation/InboxCommonTest.feature @@ -91,3 +91,15 @@ Feature: Inbox Common Test * print response Then match response.req == ['/users/test-user2'] + + Scenario: user-inboxにHTTP Signatureがないリクエストがきたら401を返す + + * def inbox = + """ + {"type": "Follow"} + """ + + Given path '/inbox' + And request inbox + When method post + Then status 401 From 8d4288247a82a602e03a7556a3abe974465b1dcf Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 14:01:11 +0900 Subject: [PATCH 096/116] =?UTF-8?q?test:=20inbox=E3=81=AEConsumes=E3=81=AE?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/federation/InboxCommonTest.kt | 11 ++++ .../federation/InboxCommonTest.feature | 53 +++++++++++++++++++ .../interfaces/api/inbox/InboxController.kt | 1 + 3 files changed, 65 insertions(+) diff --git a/src/e2eTest/kotlin/federation/InboxCommonTest.kt b/src/e2eTest/kotlin/federation/InboxCommonTest.kt index 43c204e8..3f869b2f 100644 --- a/src/e2eTest/kotlin/federation/InboxCommonTest.kt +++ b/src/e2eTest/kotlin/federation/InboxCommonTest.kt @@ -78,6 +78,17 @@ class InboxCommonTest { ) } + @Karate.Test + @TestFactory + fun `inboxにConetnt-Type application *+json以外が来たら415を返す`(): Karate { + return KarateUtil.e2eTest( + "InboxCommonTest", + "inboxにContent-Type application/json以外が来たら415を返す", + mapOf("karate.port" to port), + javaClass + ) + } + companion object { lateinit var server: MockServer diff --git a/src/e2eTest/resources/federation/InboxCommonTest.feature b/src/e2eTest/resources/federation/InboxCommonTest.feature index eec903d3..848e5630 100644 --- a/src/e2eTest/resources/federation/InboxCommonTest.feature +++ b/src/e2eTest/resources/federation/InboxCommonTest.feature @@ -103,3 +103,56 @@ Feature: Inbox Common Test And request inbox When method post Then status 401 + + + Scenario: inboxにContent-Type application/json以外が来たら415を返す + + * def inbox = + """ + {"type": "Follow"} + """ + + Given path '/inbox' + And request inbox + And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' + And header Accept = 'application/activity+json' + And header Content-Type = 'application/json' + When method post + Then status 202 + + Given path '/inbox' + And request inbox + And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' + And header Accept = 'application/activity+json' + And header Content-Type = 'application/activity+json' + When method post + Then status 202 + + Given path '/inbox' + And request inbox + And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' + And header Accept = 'application/activity+json' + And header Content-Type = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' + When method post + Then status 202 + + Given path '/inbox' + And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' + And header Accept = 'application/activity+json' + When method post + Then status 415 + + * def html = + """ + + + +""" + + Given path '/inbox' + And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' + And header Accept = 'application/activity+json' + And header Content-Type = 'text/html' + And request html + When method post + Then status 415 diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxController.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxController.kt index b2e401cc..7fa3ce18 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxController.kt @@ -15,6 +15,7 @@ interface InboxController { "application/activity+json", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" ], + consumes = ["application/json", "application/*+json"], method = [RequestMethod.POST] ) suspend fun inbox(@RequestBody string: String): ResponseEntity From aaa8bcdad89fb3ccf74ae7aa029e5a122e32c75e Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 15:50:23 +0900 Subject: [PATCH 097/116] =?UTF-8?q?test:=20FollowAcceptTest=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/e2eTest/kotlin/AssertionUtil.kt | 12 +- .../kotlin/federation/FollowAcceptTest.kt | 87 +++++++++++ .../kotlin/federation/InboxCommonTest.kt | 14 +- .../federation/FollowAcceptMockServer.feature | 140 ++++++++++++++++++ .../federation/FollowAcceptTest.feature | 29 ++++ .../InboxxCommonMockServerTest.feature | 2 +- 6 files changed, 271 insertions(+), 13 deletions(-) create mode 100644 src/e2eTest/kotlin/federation/FollowAcceptTest.kt create mode 100644 src/e2eTest/resources/federation/FollowAcceptMockServer.feature create mode 100644 src/e2eTest/resources/federation/FollowAcceptTest.feature diff --git a/src/e2eTest/kotlin/AssertionUtil.kt b/src/e2eTest/kotlin/AssertionUtil.kt index 4d559624..8083b825 100644 --- a/src/e2eTest/kotlin/AssertionUtil.kt +++ b/src/e2eTest/kotlin/AssertionUtil.kt @@ -2,10 +2,20 @@ import dev.usbharu.hideout.core.infrastructure.exposedrepository.Users import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.selectAll +import java.net.MalformedURLException +import java.net.URL object AssertionUtil { + @JvmStatic fun assertUserExist(username: String, domain: String): Boolean { + val s = try { + val url = URL(domain) + url.host + ":" + url.port.toString().takeIf { it != "-1" }.orEmpty() + } catch (e: MalformedURLException) { + domain + } + val selectAll = Users.selectAll() println(selectAll.fetchSize) @@ -13,6 +23,6 @@ object AssertionUtil { selectAll.map { "@${it[Users.name]}@${it[Users.domain]}" }.forEach { println(it) } - return Users.select { Users.name eq username and (Users.domain eq domain) }.empty().not() + return Users.select { Users.name eq username and (Users.domain eq s) }.empty().not() } } diff --git a/src/e2eTest/kotlin/federation/FollowAcceptTest.kt b/src/e2eTest/kotlin/federation/FollowAcceptTest.kt new file mode 100644 index 00000000..2f7cdf4d --- /dev/null +++ b/src/e2eTest/kotlin/federation/FollowAcceptTest.kt @@ -0,0 +1,87 @@ +package federation + +import AssertionUtil +import KarateUtil +import com.intuit.karate.core.MockServer +import com.intuit.karate.junit5.Karate +import dev.usbharu.hideout.SpringApplication +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import org.flywaydb.core.Flyway +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.TestFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.web.server.LocalServerPort +import org.springframework.transaction.annotation.Transactional +import java.net.MalformedURLException +import java.net.URL + +@SpringBootTest( + classes = [SpringApplication::class], + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT +) +@Transactional +class FollowAcceptTest { + @LocalServerPort + private var port = "" + + @Karate.Test + @TestFactory + fun `FollowAcceptTest`(): Karate { + return KarateUtil.e2eTest( + "FollowAcceptTest", "Follow Accept Test", + mapOf("karate.port" to port), + javaClass + ) + } + + companion object { + lateinit var server: MockServer + + lateinit var _remotePort: String + + @JvmStatic + fun assertUserExist(username: String, domain: String) = runBlocking { + val s = try { + val url = URL(domain) + url.host + ":" + url.port.toString().takeIf { it != "-1" }.orEmpty() + } catch (e: MalformedURLException) { + domain + } + + var check = false + + repeat(10) { + delay(1000) + check = AssertionUtil.assertUserExist(username, s) or check + if (check) { + return@repeat + } + } + + Assertions.assertTrue(check, "User @$username@$s not exist.") + } + + @JvmStatic + fun getRemotePort(): String = _remotePort + + @BeforeAll + @JvmStatic + fun beforeAll(@Autowired flyway: Flyway) { + server = MockServer.feature("classpath:federation/FollowAcceptMockServer.feature").http(0).build() + _remotePort = server.port.toString() + + flyway.clean() + flyway.migrate() + } + + @AfterAll + @JvmStatic + fun afterAll() { + server.stop() + } + } +} diff --git a/src/e2eTest/kotlin/federation/InboxCommonTest.kt b/src/e2eTest/kotlin/federation/InboxCommonTest.kt index 3f869b2f..33d595af 100644 --- a/src/e2eTest/kotlin/federation/InboxCommonTest.kt +++ b/src/e2eTest/kotlin/federation/InboxCommonTest.kt @@ -16,8 +16,6 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.web.server.LocalServerPort import org.springframework.transaction.annotation.Transactional -import java.net.MalformedURLException -import java.net.URL @SpringBootTest( classes = [SpringApplication::class], @@ -96,24 +94,17 @@ class InboxCommonTest { @JvmStatic fun assertUserExist(username: String, domain: String) = runBlocking { - val s = try { - val url = URL(domain) - url.host + ":" + url.port.toString().takeIf { it != "-1" }.orEmpty() - } catch (e: MalformedURLException) { - domain - } - var check = false repeat(10) { delay(1000) - check = AssertionUtil.assertUserExist(username, s) or check + check = AssertionUtil.assertUserExist(username, domain) or check if (check) { return@repeat } } - assertTrue(check, "User @$username@$s not exist.") + assertTrue(check, "User @$username@$domain not exist.") } @JvmStatic @@ -128,6 +119,7 @@ class InboxCommonTest { flyway.clean() flyway.migrate() } + @AfterAll @JvmStatic fun afterAll() { diff --git a/src/e2eTest/resources/federation/FollowAcceptMockServer.feature b/src/e2eTest/resources/federation/FollowAcceptMockServer.feature new file mode 100644 index 00000000..60793fde --- /dev/null +++ b/src/e2eTest/resources/federation/FollowAcceptMockServer.feature @@ -0,0 +1,140 @@ +Feature: Follow Accept Mock Server + + Background: + * def assertInbox = Java.type(`federation.FollowAcceptTest`) + * def req = {req: []} + + Scenario: pathMatches('/users/test-follower') && methodIs('get') + * def remoteUrl = 'http://localhost:' + assertInbox.getRemotePort() + * def username = 'test-follower' + * def userUrl = remoteUrl + '/users/' + username + + + * def person = + """ + { + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "toot": "http://joinmastodon.org/ns#", + "featured": { + "@id": "toot:featured", + "@type": "@id" + }, + "featuredTags": { + "@id": "toot:featuredTags", + "@type": "@id" + }, + "alsoKnownAs": { + "@id": "as:alsoKnownAs", + "@type": "@id" + }, + "movedTo": { + "@id": "as:movedTo", + "@type": "@id" + }, + "schema": "http://schema.org#", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value", + "discoverable": "toot:discoverable", + "Device": "toot:Device", + "Ed25519Signature": "toot:Ed25519Signature", + "Ed25519Key": "toot:Ed25519Key", + "Curve25519Key": "toot:Curve25519Key", + "EncryptedMessage": "toot:EncryptedMessage", + "publicKeyBase64": "toot:publicKeyBase64", + "deviceId": "toot:deviceId", + "claim": { + "@type": "@id", + "@id": "toot:claim" + }, + "fingerprintKey": { + "@type": "@id", + "@id": "toot:fingerprintKey" + }, + "identityKey": { + "@type": "@id", + "@id": "toot:identityKey" + }, + "devices": { + "@type": "@id", + "@id": "toot:devices" + }, + "messageFranking": "toot:messageFranking", + "messageType": "toot:messageType", + "cipherText": "toot:cipherText", + "suspended": "toot:suspended", + "focalPoint": { + "@container": "@list", + "@id": "toot:focalPoint" + } + } + ], + "id": #(userUrl), + "type": "Person", + "following": #(userUrl + '/following'), + "followers": #(userUrl + '/followers'), + "inbox": #(userUrl + '/inbox'), + "outbox": #(userUrl + '/outbox'), + "featured": #(userUrl + '/collections/featured'), + "featuredTags": #(userUrl + '/collections/tags'), + "preferredUsername": #(username), + "name": #(username), + "summary": "E2E Test User Jaga/Cotlin/Winter Boot/Ktol\nYonTude: https://example.com\nY(Tvvitter): https://example.com\n", + "url": #(userUrl + '/@' + username), + "manuallyApprovesFollowers": false, + "discoverable": true, + "published": "2016-03-16T00:00:00Z", + "devices": #(userUrl + '/collections/devices'), + "alsoKnownAs": [ + #( 'https://example.com/users/' + username) + ], + "publicKey": { + "id": #(userUrl + '#main-key'), + "owner": #(userUrl), + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvmtKo0xYGXR4M0LQQhK4\nBkKpRvvUxrqGV6Ew4CBHSyzdnbFsiBqHUWz4JvRiQvAPqQ4jQFpxVZCPr9xx6lJp\nx7EAAKIdVTnBBV4CYfu7QPsRqtjbB5408q+mo5oUXNs8xg2tcC42p2SJ5CRJX/fr\nOgCZwo3LC9pOBdCQZ+tiiPmWNBTNby99JZn4D/xNcwuhV04qcPoHYD9OPuxxGyzc\naVJ2mqJmvi/lewQuR8qnUIbz+Gik+xvyG6LmyuDoa1H2LDQfQXYb62G70HsYdu7a\ndObvZovytp+kkjP/cUaIYkhhOAYqAA4zCwVRY4NHK0MAMq9sMoUfNJa8U+zR9NvD\noQIDAQAB\n-----END PUBLIC KEY-----\n" + }, + "tag": [], + "attachment": [ + { + "type": "PropertyValue", + "name": "Pixib Fan-Bridge", + "value": "\u003ca href=\"https://example.com/hideout\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003eexample.com/hideout\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" + }, + { + "type": "PropertyValue", + "name": "GitHub", + "value": "\u003ca href=\"https://github.com/usbharu/hideout\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/usbharu/hideout\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" + } + ], + "endpoints": { + "sharedInbox": #(remoteUrl + '/inbox') + }, + "icon": { + "type": "Image", + "mediaType": "image/jpeg", + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/Destroyer_Vozbuzhdenyy.jpg/320px-Destroyer_Vozbuzhdenyy.jpg" + }, + "image": { + "type": "Image", + "mediaType": "image/jpeg", + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/63/Views_of_Mount_Fuji_from_%C5%8Cwakudani_20211202.jpg/320px-Views_of_Mount_Fuji_from_%C5%8Cwakudani_20211202.jpg" + } +} + """ + + * set req.req[] = '/users/' + username + * def response = person + + Scenario: pathMatches('/inbox') && methodIs('post') + * set req.req[] = '/inbox' + * def responseStatus = 202 + + Scenario: pathMatches('/internal-assertion-api/requests') && methodIs('get') + * def response = req + + Scenario: pathMatches('/internal-assertion-api/requests/deleteAll') && methodIs('post') + * set req.req = [] + * def responseStatus = 200 diff --git a/src/e2eTest/resources/federation/FollowAcceptTest.feature b/src/e2eTest/resources/federation/FollowAcceptTest.feature new file mode 100644 index 00000000..7cdb39e5 --- /dev/null +++ b/src/e2eTest/resources/federation/FollowAcceptTest.feature @@ -0,0 +1,29 @@ +Feature: Follow Accept Test + + Background: + * url baseUrl + * def assertionUtil = Java.type('AssertionUtil') + + Scenario: Follow Accept Test + + * def follow = + """ + {"type": "Follow","actor": #(remoteUrl + '/users/test-follower'),"object": #(baseUrl + '/users/test-user')} + """ + + Given path '/inbox' + And header Signature = 'keyId="https://test-hideout.usbharu.dev/users/c#pubkey", algorithm="rsa-sha256", headers="x-request-id tpp-redirect-uri digest psu-id", signature="e/91pFiI5bRffP33EMrqoI5A0xjkg3Ar0kzRGHC/1RsLrDW0zV50dHly/qJJ5xrYHRlss3+vd0mznTLBs1X0hx0uXjpfvCvwclpSi8u+sqn+Y2bcQKzf7ah0vAONQd6zeTYW7e/1kDJreP43PsJyz29KZD16Yop8nM++YeEs6C5eWiyYXKoQozXnfmTOX3y6bhxfKKQWVcxA5aLOTmTZRYTsBsTy9zn8NjDQaRI/0gcyYPqpq+2g8j2DbyJu3Z6zP6VmwbGGlQU/s9Pa7G5LqUPH/sBMSlIeqh+Hvm2pL7B3/BMFvGtTD+e2mR60BFnLIxMYx+oX4o33J2XkFIODLQ=="' + And request follow + When method post + Then status 202 + + And retry until assertionUtil.assertUserExist('test-follower',remoteUrl) + + * url remoteUrl + + Given path '/internal-assertion-api/requests' + When method get + Then status 200 + And match response.req contains ['/users/test-follower'] + + * url baseUrl diff --git a/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature b/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature index 519b7d05..6d114c04 100644 --- a/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature +++ b/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature @@ -110,7 +110,7 @@ Feature: InboxCommonMockServer } ], "endpoints": { - "sharedInbox": #(userUrl + 'inbox') + "sharedInbox": #(remoteUrl + '/inbox') }, "icon": { "type": "Image", From 2b3b2b48d387dc9986bcc179aeaf417adebc9108 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 15:52:01 +0900 Subject: [PATCH 098/116] =?UTF-8?q?test:=20FollowAcceptTest=E3=82=92?= =?UTF-8?q?=E7=84=A1=E5=8A=B9=E5=8C=96=20https://github.com/usbharu/Hideou?= =?UTF-8?q?t/issues/179#issuecomment-1837388337?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/e2eTest/kotlin/federation/FollowAcceptTest.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/e2eTest/kotlin/federation/FollowAcceptTest.kt b/src/e2eTest/kotlin/federation/FollowAcceptTest.kt index 2f7cdf4d..3c7cd02d 100644 --- a/src/e2eTest/kotlin/federation/FollowAcceptTest.kt +++ b/src/e2eTest/kotlin/federation/FollowAcceptTest.kt @@ -8,10 +8,7 @@ import dev.usbharu.hideout.SpringApplication import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.TestFactory +import org.junit.jupiter.api.* import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.web.server.LocalServerPort @@ -30,6 +27,7 @@ class FollowAcceptTest { @Karate.Test @TestFactory + @Disabled fun `FollowAcceptTest`(): Karate { return KarateUtil.e2eTest( "FollowAcceptTest", "Follow Accept Test", From b5872e5c765d4b7aea90d29e04c60af58cafb97a Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:00:05 +0900 Subject: [PATCH 099/116] =?UTF-8?q?chore:=20e2e=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=81=AE=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflows/pull-request-merge-check.yml | 65 ++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 72f62868..8503a645 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -253,7 +253,7 @@ jobs: report-tests: name: Report Tests if: success() || failure() - needs: [ unit-test,integration-test ] + needs: [ unit-test,integration-test,e2e-test ] runs-on: ubuntu-latest steps: - name: Restore Test Report @@ -330,3 +330,66 @@ jobs: uses: reviewdog/action-suggester@v1 with: github_token: ${{ secrets.GITHUB_TOKEN }} + + e2e-test: + name: E2E Test + needs: [ setup ] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Gradle Wrapper Cache + uses: actions/cache@v3.3.2 + with: + path: ~/.gradle/wrapper + key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} + + - name: Dependencies Cache + uses: actions/cache@v3.3.2 + with: + path: | + ~/.gradle/cache/jars-* + ~/.gradle/caches/transforms-* + ~/.gradle/caches/modules-* + key: gradle-dependencies-${{ hashFiles('**/*.gradle.kts') }} + restore-keys: gradle-dependencies- + + - name: Cache + uses: actions/cache@v3.3.2 + with: + path: | + ~/.gradle/caches/build-cache-* + ~/.gradle/caches/[0-9]*.* + .gradle + key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}- + + - name: Build Cache + uses: actions/cache@v3.3.2 + with: + path: | + build + key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }} + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: MongoDB in GitHub Actions + uses: supercharge/mongodb-github-action@1.10.0 + with: + mongodb-version: latest + + - name: E2E Test + uses: gradle/gradle-build-action@v2.8.1 + with: + arguments: e2eTest + + - name: Save Test Report + uses: actions/cache/save@v3 + with: + path: build/test-results + key: e2e-test-report-${{ github.sha }} From 96196cf7ba0626ef951dd7da655671801845c143 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:14:17 +0900 Subject: [PATCH 100/116] =?UTF-8?q?chore:=20e2e=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=81=AE=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-merge-check.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 8503a645..0e6c9b10 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -234,7 +234,7 @@ jobs: - name: Run Kover uses: gradle/gradle-build-action@v2.8.1 with: - arguments: koverXmlReport -x integrationTest + arguments: koverXmlReport -x integrationTest -x e2eTest - name: Add coverage report to PR if: always() @@ -268,6 +268,12 @@ jobs: path: build/test-results key: integration-test-report-${{ github.sha }} + - name: Restore Test Report + uses: actions/cache/restore@v3 + with: + path: build/test-results + key: e2e-test-report-${{ github.sha }} + - name: JUnit Test Report uses: mikepenz/action-junit-report@v2 with: From 96402acf1cd4c1a6fd69b852edc0a4ece138965c Mon Sep 17 00:00:00 2001 From: usbharu Date: Sun, 3 Dec 2023 16:15:55 +0900 Subject: [PATCH 101/116] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../hideout/activitypub/service/inbox/InboxJobProcessor.kt | 1 - .../dev/usbharu/hideout/application/config/SecurityConfig.kt | 2 -- .../core/infrastructure/httpsignature/HttpRequestMixIn.kt | 5 ----- 3 files changed, 8 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt index 91c3f4ed..b707e5e6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt @@ -81,7 +81,6 @@ class InboxJobProcessor( } override suspend fun process(param: InboxJobParam) { - val jsonNode = objectMapper.readTree(param.json) logger.info("START Process inbox. type: {}", param.type) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index aa60fa16..c0f0bbff 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -63,7 +63,6 @@ import java.security.interfaces.RSAPrivateKey import java.security.interfaces.RSAPublicKey import java.util.* - @EnableWebSecurity(debug = false) @Configuration @Suppress("FunctionMaxLength", "TooManyFunctions") @@ -285,7 +284,6 @@ data class JwkConfig( val privateKey: String ) - @Configuration class PostSecurityConfig( val auth: AuthenticationManagerBuilder, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt index 3686d3c9..4f998d91 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt @@ -11,23 +11,18 @@ import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest import java.net.URL - @JsonDeserialize(using = HttpRequestDeserializer::class) @JsonSubTypes abstract class HttpRequestMixIn class HttpRequestDeserializer : JsonDeserializer() { override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): HttpRequest { - val readTree: JsonNode = p.codec.readTree(p) - - return HttpRequest( URL(readTree["url"].textValue()), HttpHeaders(emptyMap()), HttpMethod.valueOf(readTree["method"].textValue()) ) } - } From 3676961a40bc63125f518f68792e15db52e5b5bf Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:22:06 +0900 Subject: [PATCH 102/116] =?UTF-8?q?chore:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=83=AC=E3=83=9D=E3=83=BC=E3=83=88=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-merge-check.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 0e6c9b10..4c8f5763 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -116,6 +116,7 @@ jobs: arguments: test - name: Save Test Report + if: always() uses: actions/cache/save@v3 with: path: build/test-results @@ -179,6 +180,7 @@ jobs: arguments: integrationTest - name: Save Test Report + if: always() uses: actions/cache/save@v3 with: path: build/test-results @@ -395,6 +397,7 @@ jobs: arguments: e2eTest - name: Save Test Report + if: always() uses: actions/cache/save@v3 with: path: build/test-results From 488659e767494e2f9e96a0f78e7cc2ab3fb10158 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:39:52 +0900 Subject: [PATCH 103/116] =?UTF-8?q?chore:=20e2e=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E7=94=A8=E3=81=ABChrome=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-merge-check.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 4c8f5763..99d76f1d 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -391,6 +391,9 @@ jobs: with: mongodb-version: latest + - name: Setup Chrome + uses: browser-actions/setup-chrome@v1.4.0 + - name: E2E Test uses: gradle/gradle-build-action@v2.8.1 with: From 32bb44c8a1c721883adb63e149b5302cd40bb6b6 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:14:22 +0900 Subject: [PATCH 104/116] =?UTF-8?q?chore:=20e2e=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E7=94=A8=E3=81=ABChrome=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-merge-check.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 99d76f1d..0e5d0973 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -51,7 +51,7 @@ jobs: with: path: | build - key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }} + key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('**/*.kt') }}-${{ github.sha }} - name: Set up JDK 17 uses: actions/setup-java@v3 @@ -394,11 +394,15 @@ jobs: - name: Setup Chrome uses: browser-actions/setup-chrome@v1.4.0 + - name: Add Path + run: echo ${{ steps.setup-chrome.outputs.chrome-path }} >> $GITHUB_PATH + - name: E2E Test uses: gradle/gradle-build-action@v2.8.1 with: arguments: e2eTest + - name: Save Test Report if: always() uses: actions/cache/save@v3 From 78622b5827794ed2f961a2d81d15ceeafb107a85 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:20:13 +0900 Subject: [PATCH 105/116] style: fix lint --- .../hideout/activitypub/domain/model/Person.kt | 14 ++++++++++++-- .../activitypub/service/inbox/InboxJobProcessor.kt | 1 + .../service/objects/user/APUserService.kt | 8 +++----- .../hideout/application/config/SecurityConfig.kt | 7 +++---- .../HttpSignatureUserDetailsService.kt | 9 +++------ .../core/service/resource/InMemoryCacheManager.kt | 1 + .../util/SpringSecurityKotlinDslExtension.kt | 5 ++--- 7 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt index 4791fa68..c04ba736 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt @@ -22,9 +22,13 @@ constructor( override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Person) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false + other as Person + + if (name != other.name) return false + if (id != other.id) return false if (preferredUsername != other.preferredUsername) return false if (summary != other.summary) return false if (inbox != other.inbox) return false @@ -33,20 +37,26 @@ constructor( if (icon != other.icon) return false if (publicKey != other.publicKey) return false if (endpoints != other.endpoints) return false + if (followers != other.followers) return false + if (following != other.following) return false return true } override fun hashCode(): Int { var result = super.hashCode() + result = 31 * result + name.hashCode() + result = 31 * result + id.hashCode() result = 31 * result + (preferredUsername?.hashCode() ?: 0) result = 31 * result + (summary?.hashCode() ?: 0) result = 31 * result + inbox.hashCode() result = 31 * result + outbox.hashCode() result = 31 * result + url.hashCode() result = 31 * result + (icon?.hashCode() ?: 0) - result = 31 * result + (publicKey?.hashCode() ?: 0) + result = 31 * result + publicKey.hashCode() result = 31 * result + endpoints.hashCode() + result = 31 * result + (followers?.hashCode() ?: 0) + result = 31 * result + (following?.hashCode() ?: 0) return result } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt index b707e5e6..65220c41 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt @@ -56,6 +56,7 @@ class InboxJobProcessor( } } + @Suppress("TooGenericExceptionCaught") val verify = try { signatureVerifier.verify( httpRequest, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt index 47568b8d..fff97e01 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt @@ -95,15 +95,13 @@ class APUserServiceImpl( name = person.preferredUsername ?: throw IllegalActivityPubObjectException("preferredUsername is null"), domain = id.substringAfter("://").substringBefore("/"), - screenName = person.name - ?: throw IllegalActivityPubObjectException("preferredUsername is null"), + screenName = person.name, description = person.summary.orEmpty(), inbox = person.inbox, outbox = person.outbox, url = id, - publicKey = person.publicKey?.publicKeyPem - ?: throw IllegalActivityPubObjectException("publicKey is null"), - keyId = person.publicKey?.id ?: throw IllegalActivityPubObjectException("publicKey keyId is null"), + publicKey = person.publicKey.publicKeyPem, + keyId = person.publicKey.id, following = person.following, followers = person.followers, sharedInbox = person.endpoints["sharedInbox"] diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index c0f0bbff..dead45f0 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -84,7 +84,9 @@ class SecurityConfig { http { securityMatcher("/users/*/posts/*") addFilterAt(httpSignatureFilter) - addFilterBefore(ExceptionTranslationFilter(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))) + addFilterBefore( + ExceptionTranslationFilter(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) + ) authorizeHttpRequests { authorize(anyRequest, permitAll) } @@ -160,7 +162,6 @@ class SecurityConfig { } oauth2ResourceServer { jwt { - } } } @@ -172,7 +173,6 @@ class SecurityConfig { fun defaultSecurityFilterChain(http: HttpSecurity): SecurityFilterChain { http { authorizeHttpRequests { - authorize("/error", permitAll) authorize("/login", permitAll) authorize(GET, "/.well-known/**", permitAll) @@ -200,7 +200,6 @@ class SecurityConfig { } formLogin { - } csrf { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt index 3acc12f6..a75fe934 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt @@ -27,13 +27,10 @@ class HttpSignatureUserDetailsService( ) : AuthenticationUserDetailsService { override fun loadUserDetails(token: PreAuthenticatedAuthenticationToken): UserDetails = runBlocking { - if (token.principal !is String) { - throw IllegalStateException("Token is not String") - } + check(token.principal is String) { "Token is not String" } val credentials = token.credentials - if (credentials !is HttpRequest) { - throw IllegalStateException("Credentials is not HttpRequest") - } + + check(credentials is HttpRequest) { "Credentials is not HttpRequest" } val keyId = token.principal as String val findByKeyId = transaction.transaction { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt index d5ca028d..6efbc980 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt @@ -29,6 +29,7 @@ class InMemoryCacheManager : CacheManager { } } if (needRunBlock) { + @Suppress("TooGenericExceptionCaught") val processed = try { block() } catch (e: Exception) { diff --git a/src/main/kotlin/dev/usbharu/hideout/util/SpringSecurityKotlinDslExtension.kt b/src/main/kotlin/dev/usbharu/hideout/util/SpringSecurityKotlinDslExtension.kt index 42159643..52a2f486 100644 --- a/src/main/kotlin/dev/usbharu/hideout/util/SpringSecurityKotlinDslExtension.kt +++ b/src/main/kotlin/dev/usbharu/hideout/util/SpringSecurityKotlinDslExtension.kt @@ -7,7 +7,6 @@ import org.springframework.security.web.access.intercept.RequestAuthorizationCon fun AuthorizeHttpRequestsDsl.hasScope(scope: String): AuthorizationManager = hasAuthority("SCOPE_$scope") +@Suppress("SpreadOperator") fun AuthorizeHttpRequestsDsl.hasAnyScope(vararg scopes: String): AuthorizationManager = - hasAnyAuthority( - *scopes.map { "SCOPE_$it" }.toTypedArray() - ) + hasAnyAuthority(*scopes.map { "SCOPE_$it" }.toTypedArray()) From 1fdb5895a70c65b89c6d9a463ccb2624b357f736 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:21:23 +0900 Subject: [PATCH 106/116] =?UTF-8?q?chore:=20=E3=82=B9=E3=83=86=E3=83=83?= =?UTF-8?q?=E3=83=97=E5=90=8D=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-merge-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 0e5d0973..1a1366a4 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -391,7 +391,7 @@ jobs: with: mongodb-version: latest - - name: Setup Chrome + - name: setup-chrome uses: browser-actions/setup-chrome@v1.4.0 - name: Add Path From bad5a2e268e9dfa39b9cef344d869e4d32756f32 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:26:10 +0900 Subject: [PATCH 107/116] =?UTF-8?q?chore:=20=E3=82=B9=E3=83=86=E3=83=83?= =?UTF-8?q?=E3=83=97id=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-merge-check.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 1a1366a4..7d1a26f2 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -392,6 +392,7 @@ jobs: mongodb-version: latest - name: setup-chrome + id: setup-chrome uses: browser-actions/setup-chrome@v1.4.0 - name: Add Path From 3f5a573fb31e42a670f46d444fa404f7760a0623 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 4 Dec 2023 18:33:55 +0900 Subject: [PATCH 108/116] =?UTF-8?q?test:=20headless=20chrome=E3=81=AE?= =?UTF-8?q?=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E5=A4=89?= =?UTF-8?q?=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/e2eTest/resources/oauth2/Oauth2LoginTest.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature b/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature index f330c369..af203344 100644 --- a/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature +++ b/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature @@ -2,7 +2,7 @@ Feature: OAuth2 Login Test Background: * url baseUrl - * configure driver = { type: 'chrome' } + * configure driver = { type: 'chrome',start: true, headless: true, showDriverLog: true, addOptions: [ '--headless=new' ] } Scenario: スコープwrite readを持ったトークンの作成 From a8da0f5366b6b283d406997f38d7e7db36062992 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:17:19 +0900 Subject: [PATCH 109/116] =?UTF-8?q?feat:=20S3=E4=BB=A5=E5=A4=96=E3=81=AB?= =?UTF-8?q?=E3=82=82=E3=83=A1=E3=83=87=E3=82=A3=E3=82=A2=E3=82=92=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=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/application/config/AwsConfig.kt | 2 +- .../application/config/SpringConfig.kt | 13 +++-- .../media/LocalFileSystemMediaDataStore.kt | 58 +++++++++++++++++++ .../core/service/media/S3MediaDataStore.kt | 32 +++++----- 4 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/AwsConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/AwsConfig.kt index 67820efd..45db658d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/AwsConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/AwsConfig.kt @@ -10,7 +10,7 @@ import java.net.URI @Configuration class AwsConfig { @Bean - fun s3Client(awsConfig: StorageConfig): S3Client { + fun s3Client(awsConfig: S3StorageConfig): S3Client { return S3Client.builder() .endpointOverride(URI.create(awsConfig.endpoint)) .region(Region.of(awsConfig.region)) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt index 89275999..dfbd3c27 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt @@ -14,7 +14,7 @@ class SpringConfig { lateinit var config: ApplicationConfig @Autowired - lateinit var storageConfig: StorageConfig + lateinit var s3StorageConfig: S3StorageConfig @Bean fun requestLoggingFilter(): CommonsRequestLoggingFilter { @@ -33,9 +33,8 @@ data class ApplicationConfig( val url: URL ) -@ConfigurationProperties("hideout.storage") -data class StorageConfig( - val useS3: Boolean, +@ConfigurationProperties("hideout.storage.s3") +data class S3StorageConfig( val endpoint: String, val publicUrl: String, val bucket: String, @@ -44,6 +43,12 @@ data class StorageConfig( val secretKey: String ) +@ConfigurationProperties("hideout.storage.local") +data class LocalStorageConfig( + val path: String, + val publicUrl: String? +) + @ConfigurationProperties("hideout.character-limit") data class CharacterLimit( val general: General = General(), diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt new file mode 100644 index 00000000..4c1e4e45 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt @@ -0,0 +1,58 @@ +package dev.usbharu.hideout.core.service.media + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.config.LocalStorageConfig +import org.springframework.stereotype.Service +import java.nio.file.Path +import kotlin.io.path.copyTo +import kotlin.io.path.deleteIfExists +import kotlin.io.path.outputStream + +@Service +class LocalFileSystemMediaDataStore( + applicationConfig: ApplicationConfig, + localStorageConfig: LocalStorageConfig +) : MediaDataStore { + + private val savePath: Path = Path.of(localStorageConfig.path) + + private val publicUrl = localStorageConfig.publicUrl ?: "${applicationConfig.url}/files/" + + override suspend fun save(dataMediaSave: MediaSave): SavedMedia { + val fileSavePath = buildSavePath(savePath, dataMediaSave.name) + val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataMediaSave.name) + + + + dataMediaSave.thumbnailInputStream?.inputStream()?.buffered() + ?.transferTo(thumbnailSavePath.outputStream().buffered()) + dataMediaSave.fileInputStream.inputStream().buffered().transferTo(fileSavePath.outputStream().buffered()) + + return SuccessSavedMedia( + dataMediaSave.name, + publicUrl + dataMediaSave.name, + publicUrl + "thumbnail-" + dataMediaSave.name + ) + } + + override suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia { + val fileSavePath = buildSavePath(savePath, dataSaveRequest.name) + val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataSaveRequest.name) + + dataSaveRequest.filePath.copyTo(fileSavePath) + dataSaveRequest.thumbnailPath?.copyTo(thumbnailSavePath) + + return SuccessSavedMedia( + dataSaveRequest.name, + publicUrl + dataSaveRequest.name, + publicUrl + "thumbnail-" + dataSaveRequest.name + ) + } + + override suspend fun delete(id: String) { + buildSavePath(savePath, id).deleteIfExists() + buildSavePath(savePath, "thumbnail-$id").deleteIfExists() + } + + private fun buildSavePath(savePath: Path, name: String): Path = savePath.resolve(name) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/S3MediaDataStore.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/S3MediaDataStore.kt index 4377fdc0..4220162c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/S3MediaDataStore.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/S3MediaDataStore.kt @@ -1,6 +1,6 @@ package dev.usbharu.hideout.core.service.media -import dev.usbharu.hideout.application.config.StorageConfig +import dev.usbharu.hideout.application.config.S3StorageConfig import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -14,16 +14,16 @@ import software.amazon.awssdk.services.s3.model.GetUrlRequest import software.amazon.awssdk.services.s3.model.PutObjectRequest @Service -class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig: StorageConfig) : MediaDataStore { +class S3MediaDataStore(private val s3Client: S3Client, private val s3StorageConfig: S3StorageConfig) : MediaDataStore { override suspend fun save(dataMediaSave: MediaSave): SavedMedia { val fileUploadRequest = PutObjectRequest.builder() - .bucket(storageConfig.bucket) + .bucket(s3StorageConfig.bucket) .key(dataMediaSave.name) .build() val thumbnailKey = "thumbnail-${dataMediaSave.name}" val thumbnailUploadRequest = PutObjectRequest.builder() - .bucket(storageConfig.bucket) + .bucket(s3StorageConfig.bucket) .key(thumbnailKey) .build() @@ -36,7 +36,7 @@ class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig RequestBody.fromBytes(dataMediaSave.thumbnailInputStream) ) s3Client.utilities() - .getUrl(GetUrlRequest.builder().bucket(storageConfig.bucket).key(thumbnailKey).build()) + .getUrl(GetUrlRequest.builder().bucket(s3StorageConfig.bucket).key(thumbnailKey).build()) } else { null } @@ -44,14 +44,14 @@ class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig async { s3Client.putObject(fileUploadRequest, RequestBody.fromBytes(dataMediaSave.fileInputStream)) s3Client.utilities() - .getUrl(GetUrlRequest.builder().bucket(storageConfig.bucket).key(dataMediaSave.name).build()) + .getUrl(GetUrlRequest.builder().bucket(s3StorageConfig.bucket).key(dataMediaSave.name).build()) } ) } return SuccessSavedMedia( name = dataMediaSave.name, - url = "${storageConfig.publicUrl}/${storageConfig.bucket}/${dataMediaSave.name}", - thumbnailUrl = "${storageConfig.publicUrl}/${storageConfig.bucket}/$thumbnailKey" + url = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${dataMediaSave.name}", + thumbnailUrl = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/$thumbnailKey" ) } @@ -59,19 +59,19 @@ class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig logger.info("MEDIA upload. {}", dataSaveRequest.name) val fileUploadRequest = PutObjectRequest.builder() - .bucket(storageConfig.bucket) + .bucket(s3StorageConfig.bucket) .key(dataSaveRequest.name) .build() - logger.info("MEDIA upload. bucket: {} key: {}", storageConfig.bucket, dataSaveRequest.name) + logger.info("MEDIA upload. bucket: {} key: {}", s3StorageConfig.bucket, dataSaveRequest.name) val thumbnailKey = "thumbnail-${dataSaveRequest.name}" val thumbnailUploadRequest = PutObjectRequest.builder() - .bucket(storageConfig.bucket) + .bucket(s3StorageConfig.bucket) .key(thumbnailKey) .build() - logger.info("MEDIA upload. bucket: {} key: {}", storageConfig.bucket, thumbnailKey) + logger.info("MEDIA upload. bucket: {} key: {}", s3StorageConfig.bucket, thumbnailKey) withContext(Dispatchers.IO) { awaitAll( @@ -92,8 +92,8 @@ class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig } val successSavedMedia = SuccessSavedMedia( name = dataSaveRequest.name, - url = "${storageConfig.publicUrl}/${storageConfig.bucket}/${dataSaveRequest.name}", - thumbnailUrl = "${storageConfig.publicUrl}/${storageConfig.bucket}/$thumbnailKey" + url = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${dataSaveRequest.name}", + thumbnailUrl = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/$thumbnailKey" ) logger.info("SUCCESS Media upload. {}", dataSaveRequest.name) @@ -108,9 +108,9 @@ class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig } override suspend fun delete(id: String) { - val fileDeleteRequest = DeleteObjectRequest.builder().bucket(storageConfig.bucket).key(id).build() + val fileDeleteRequest = DeleteObjectRequest.builder().bucket(s3StorageConfig.bucket).key(id).build() val thumbnailDeleteRequest = - DeleteObjectRequest.builder().bucket(storageConfig.bucket).key("thumbnail-$id").build() + DeleteObjectRequest.builder().bucket(s3StorageConfig.bucket).key("thumbnail-$id").build() s3Client.deleteObject(fileDeleteRequest) s3Client.deleteObject(thumbnailDeleteRequest) } From b56c5410aa523fc1c11bc4a234828a76aa5022c1 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:24:36 +0900 Subject: [PATCH 110/116] =?UTF-8?q?doc:=20LocalFileSystemMediaDataStore.kt?= =?UTF-8?q?=E3=81=AB=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../media/LocalFileSystemMediaDataStore.kt | 14 +++++++++++ .../core/service/media/MediaDataStore.kt | 24 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt index 4c1e4e45..0e0ef8cb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt @@ -9,6 +9,15 @@ import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream @Service +/** + * ローカルファイルシステムにメディアを保存します + * + * @constructor + * ApplicationConfigとLocalStorageConfigをもとに作成 + * + * @param applicationConfig ApplicationConfig + * @param localStorageConfig LocalStorageConfig + */ class LocalFileSystemMediaDataStore( applicationConfig: ApplicationConfig, localStorageConfig: LocalStorageConfig @@ -49,6 +58,11 @@ class LocalFileSystemMediaDataStore( ) } + /** + * メディアを削除します。サムネイルも削除されます。 + * + * @param id 削除するメディアのid [SuccessSavedMedia.name]を指定します。 + */ override suspend fun delete(id: String) { buildSavePath(savePath, id).deleteIfExists() buildSavePath(savePath, "thumbnail-$id").deleteIfExists() diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaDataStore.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaDataStore.kt index 03ee3e9f..478ddc50 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaDataStore.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaDataStore.kt @@ -1,7 +1,31 @@ package dev.usbharu.hideout.core.service.media +/** + * メディアを保存するインタフェース + * + */ interface MediaDataStore { + /** + * InputStreamを使用してメディアを保存します + * + * @param dataMediaSave FileとThumbnailのinputStream + * @return 保存されたメディア + */ suspend fun save(dataMediaSave: MediaSave): SavedMedia + + /** + * 一時ファイルのパスを使用してメディアを保存します + * + * @param dataSaveRequest FileとThumbnailのパス + * @return 保存されたメディア + */ suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia + + /** + * メディアを削除します + * 実装はサムネイル、メタデータなども削除するべきです。 + * + * @param id 削除するメディアのid 通常は[SuccessSavedMedia.name]を指定します。 + */ suspend fun delete(id: String) } From 9cdbe195a6029905d4cc76825db3bcb7e1d22ea5 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:29:54 +0900 Subject: [PATCH 111/116] =?UTF-8?q?doc:=20LocalStorageConfig=E3=81=AB?= =?UTF-8?q?=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1=E3=83=B3=E3=83=88=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/usbharu/hideout/application/config/SpringConfig.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt index dfbd3c27..9dfe2f5e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt @@ -43,6 +43,13 @@ data class S3StorageConfig( val secretKey: String ) + +/** + * メディアの保存にローカルファイルシステムを使用する際のコンフィグ + * + * @property path フォゾンする場所へのパス。 /から始めると絶対パスとなります。 + * @property publicUrl 公開用URL 省略可能 指定するとHideoutがファイルを配信しなくなります。 + */ @ConfigurationProperties("hideout.storage.local") data class LocalStorageConfig( val path: String, From 037bbd9da6c36fd160cc25794a8d854f7cd013da Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:39:31 +0900 Subject: [PATCH 112/116] =?UTF-8?q?refactor:=20LocalFileSystemMediaDataSto?= =?UTF-8?q?re.kt=E3=81=AB=E3=83=AD=E3=82=B0=E3=81=A8=E3=82=A8=E3=83=A9?= =?UTF-8?q?=E3=83=BC=E3=83=8F=E3=83=B3=E3=83=89=E3=83=AA=E3=83=B3=E3=82=B0?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../media/LocalFileSystemMediaDataStore.kt | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt index 0e0ef8cb..3102fea5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt @@ -2,6 +2,7 @@ package dev.usbharu.hideout.core.service.media import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.LocalStorageConfig +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import java.nio.file.Path import kotlin.io.path.copyTo @@ -45,12 +46,22 @@ class LocalFileSystemMediaDataStore( } override suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia { + logger.info("START Media upload. {}", dataSaveRequest.name) val fileSavePath = buildSavePath(savePath, dataSaveRequest.name) val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataSaveRequest.name) - dataSaveRequest.filePath.copyTo(fileSavePath) - dataSaveRequest.thumbnailPath?.copyTo(thumbnailSavePath) + val fileSavePathString = fileSavePath.toAbsolutePath().toString() + logger.info("MEDIA save. path: {}", fileSavePathString) + try { + dataSaveRequest.filePath.copyTo(fileSavePath) + dataSaveRequest.thumbnailPath?.copyTo(thumbnailSavePath) + } catch (e: Exception) { + logger.warn("FAILED to Save the media.", e) + return FaildSavedMedia("FAILED to Save the media.", "Failed copy to path: $fileSavePathString", e) + } + + logger.info("SUCCESS Media upload. {}", dataSaveRequest.name) return SuccessSavedMedia( dataSaveRequest.name, publicUrl + dataSaveRequest.name, @@ -64,9 +75,18 @@ class LocalFileSystemMediaDataStore( * @param id 削除するメディアのid [SuccessSavedMedia.name]を指定します。 */ override suspend fun delete(id: String) { - buildSavePath(savePath, id).deleteIfExists() - buildSavePath(savePath, "thumbnail-$id").deleteIfExists() + logger.info("START Media delete. id: {}", id) + try { + buildSavePath(savePath, id).deleteIfExists() + buildSavePath(savePath, "thumbnail-$id").deleteIfExists() + } catch (e: Exception) { + logger.warn("FAILED Media delete. id: {}", id, e) + } } private fun buildSavePath(savePath: Path, name: String): Path = savePath.resolve(name) + + companion object { + private val logger = LoggerFactory.getLogger(LocalFileSystemMediaDataStore::class.java) + } } From 21e91e58a7e1db7f01448c3a64bb8d04b2a62af7 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 6 Dec 2023 15:31:50 +0900 Subject: [PATCH 113/116] =?UTF-8?q?fix:=20=E3=83=AA=E3=82=BD=E3=83=BC?= =?UTF-8?q?=E3=82=B9=E3=81=AE=E3=82=AF=E3=83=AD=E3=83=BC=E3=82=BA=E3=82=92?= =?UTF-8?q?=E8=A1=8C=E3=81=86=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../media/LocalFileSystemMediaDataStore.kt | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt index 3102fea5..420e82bf 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt @@ -5,7 +5,9 @@ import dev.usbharu.hideout.application.config.LocalStorageConfig import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import java.nio.file.Path +import java.nio.file.StandardOpenOption import kotlin.io.path.copyTo +import kotlin.io.path.createDirectories import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream @@ -20,28 +22,43 @@ import kotlin.io.path.outputStream * @param localStorageConfig LocalStorageConfig */ class LocalFileSystemMediaDataStore( - applicationConfig: ApplicationConfig, - localStorageConfig: LocalStorageConfig + applicationConfig: ApplicationConfig, localStorageConfig: LocalStorageConfig ) : MediaDataStore { - private val savePath: Path = Path.of(localStorageConfig.path) + private val savePath: Path = Path.of(localStorageConfig.path).toAbsolutePath() private val publicUrl = localStorageConfig.publicUrl ?: "${applicationConfig.url}/files/" + init { + savePath.createDirectories() + } + override suspend fun save(dataMediaSave: MediaSave): SavedMedia { val fileSavePath = buildSavePath(savePath, dataMediaSave.name) val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataMediaSave.name) + dataMediaSave.thumbnailInputStream?.inputStream()?.use { + it.buffered().use { bufferedInputStream -> + thumbnailSavePath.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) + .use { outputStream -> + outputStream.buffered().use { + bufferedInputStream.transferTo(it) + } + } + } + } - dataMediaSave.thumbnailInputStream?.inputStream()?.buffered() - ?.transferTo(thumbnailSavePath.outputStream().buffered()) - dataMediaSave.fileInputStream.inputStream().buffered().transferTo(fileSavePath.outputStream().buffered()) + + dataMediaSave.fileInputStream.inputStream().use { + it.buffered().use { bufferedInputStream -> + fileSavePath.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) + .use { outputStream -> outputStream.buffered().use { bufferedInputStream.transferTo(it) } } + } + } return SuccessSavedMedia( - dataMediaSave.name, - publicUrl + dataMediaSave.name, - publicUrl + "thumbnail-" + dataMediaSave.name + dataMediaSave.name, publicUrl + dataMediaSave.name, publicUrl + "thumbnail-" + dataMediaSave.name ) } @@ -63,9 +80,7 @@ class LocalFileSystemMediaDataStore( logger.info("SUCCESS Media upload. {}", dataSaveRequest.name) return SuccessSavedMedia( - dataSaveRequest.name, - publicUrl + dataSaveRequest.name, - publicUrl + "thumbnail-" + dataSaveRequest.name + dataSaveRequest.name, publicUrl + dataSaveRequest.name, publicUrl + "thumbnail-" + dataSaveRequest.name ) } From 39841b9d88ff7e8fa8bcc376fbf6598f56923113 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 6 Dec 2023 15:32:15 +0900 Subject: [PATCH 114/116] =?UTF-8?q?test:=20LocalFileSystemMediaDataStore.k?= =?UTF-8?q?t=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + .../LocalFileSystemMediaDataStoreTest.kt | 121 ++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/test/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStoreTest.kt diff --git a/.gitignore b/.gitignore index 0d7dbff3..1f876f7f 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,5 @@ out/ /tomcat/ /tomcat-e2e/ /e2eTest.log +/files/test-media-29599b059-06e1-49eb-91b8-1405244e57ce.png +/files/thumbnail-test-media-29599b059-06e1-49eb-91b8-1405244e57ce.png diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStoreTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStoreTest.kt new file mode 100644 index 00000000..e2854912 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStoreTest.kt @@ -0,0 +1,121 @@ +package dev.usbharu.hideout.core.service.media + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.config.LocalStorageConfig +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.net.URL +import java.nio.file.Path +import java.util.* +import kotlin.io.path.readBytes +import kotlin.io.path.toPath + +class LocalFileSystemMediaDataStoreTest { + + private val path = String.javaClass.classLoader.getResource("400x400.png")?.toURI()?.toPath()!! + + @Test + fun `save inputStreamを使用して正常に保存できる`() = runTest { + val applicationConfig = ApplicationConfig(URL("https://example.com")) + val storageConfig = LocalStorageConfig("files", null) + + val localFileSystemMediaDataStore = LocalFileSystemMediaDataStore(applicationConfig, storageConfig) + + val fileInputStream = path.readBytes() + + assertThat(fileInputStream.size).isNotEqualTo(0) + + val mediaSave = MediaSave( + "test-media-1${UUID.randomUUID()}.png", + "", + fileInputStream, + fileInputStream + ) + + val save = localFileSystemMediaDataStore.save(mediaSave) + + assertThat(save).isInstanceOf(SuccessSavedMedia::class.java) + + save as SuccessSavedMedia + + assertThat(Path.of("files").toAbsolutePath().resolve(save.name)) + .exists() + .hasSize(fileInputStream.size.toLong()) + assertThat(Path.of("files").toAbsolutePath().resolve("thumbnail-" + save.name)) + .exists() + .hasSize(fileInputStream.size.toLong()) + } + + @Test + fun 一時ファイルを使用して正常に保存できる() = runTest { + val applicationConfig = ApplicationConfig(URL("https://example.com")) + val storageConfig = LocalStorageConfig("files", null) + + val localFileSystemMediaDataStore = LocalFileSystemMediaDataStore(applicationConfig, storageConfig) + + val fileInputStream = path.readBytes() + + assertThat(fileInputStream.size).isNotEqualTo(0) + + val saveRequest = MediaSaveRequest( + "test-media-2${UUID.randomUUID()}.png", + "", + path, + path + ) + + val save = localFileSystemMediaDataStore.save(saveRequest) + + assertThat(save).isInstanceOf(SuccessSavedMedia::class.java) + + save as SuccessSavedMedia + + assertThat(Path.of("files").toAbsolutePath().resolve(save.name)) + .exists() + .hasSize(fileInputStream.size.toLong()) + assertThat(Path.of("files").toAbsolutePath().resolve("thumbnail-" + save.name)) + .exists() + .hasSize(fileInputStream.size.toLong()) + } + + @Test + fun idを使用して削除できる() = runTest { + val applicationConfig = ApplicationConfig(URL("https://example.com")) + val storageConfig = LocalStorageConfig("files", null) + + val localFileSystemMediaDataStore = LocalFileSystemMediaDataStore(applicationConfig, storageConfig) + + val fileInputStream = path.readBytes() + + assertThat(fileInputStream.size).isNotEqualTo(0) + + val saveRequest = MediaSaveRequest( + "test-media-2${UUID.randomUUID()}.png", + "", + path, + path + ) + + val save = localFileSystemMediaDataStore.save(saveRequest) + + assertThat(save).isInstanceOf(SuccessSavedMedia::class.java) + + save as SuccessSavedMedia + + assertThat(Path.of("files").toAbsolutePath().resolve(save.name)) + .exists() + .hasSize(fileInputStream.size.toLong()) + assertThat(Path.of("files").toAbsolutePath().resolve("thumbnail-" + save.name)) + .exists() + .hasSize(fileInputStream.size.toLong()) + + + localFileSystemMediaDataStore.delete(save.name) + + assertThat(Path.of("files").toAbsolutePath().resolve(save.name)) + .doesNotExist() + assertThat(Path.of("files").toAbsolutePath().resolve("thumbnail-" + save.name)) + .doesNotExist() + } +} From bc186a4433b924d3af7c1fe11e4300b0fb823834 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:15:06 +0900 Subject: [PATCH 115/116] =?UTF-8?q?chore:=20gitignore=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 1f876f7f..84c07d25 100644 --- a/.gitignore +++ b/.gitignore @@ -42,5 +42,4 @@ out/ /tomcat/ /tomcat-e2e/ /e2eTest.log -/files/test-media-29599b059-06e1-49eb-91b8-1405244e57ce.png -/files/thumbnail-test-media-29599b059-06e1-49eb-91b8-1405244e57ce.png +/files/ From aca1be114d4fb28bdee316e5949f191096ef9b9f Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:15:38 +0900 Subject: [PATCH 116/116] =?UTF-8?q?feat:=20Spring=20Configuration=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/e2eTest/resources/application.yml | 8 +------- src/intTest/resources/application.yml | 8 +------- .../dev/usbharu/hideout/application/config/AwsConfig.kt | 2 ++ .../usbharu/hideout/application/config/SpringConfig.kt | 8 ++++---- .../core/service/media/LocalFileSystemMediaDataStore.kt | 2 ++ .../hideout/core/service/media/S3MediaDataStore.kt | 2 ++ 6 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/e2eTest/resources/application.yml b/src/e2eTest/resources/application.yml index 1013de15..330660e2 100644 --- a/src/e2eTest/resources/application.yml +++ b/src/e2eTest/resources/application.yml @@ -8,13 +8,7 @@ hideout: private-key: "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKjMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvuNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulgp2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlRZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwiVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskVlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83HmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwYdgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cwta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2TN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPvt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDUAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISLDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnKxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEAmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfzet6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhrVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicDTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cncdn/RsYEONbwQSjIfMPkvxF+8HQ==" public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB" storage: - use-s3: true - endpoint: "http://localhost:8082/test-hideout" - public-url: "http://localhost:8082/test-hideout" - bucket: "test-hideout" - region: "auto" - access-key: "" - secret-key: "" + type: local spring: flyway: diff --git a/src/intTest/resources/application.yml b/src/intTest/resources/application.yml index b40fcd91..c73fc1f3 100644 --- a/src/intTest/resources/application.yml +++ b/src/intTest/resources/application.yml @@ -8,13 +8,7 @@ hideout: private-key: "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKjMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvuNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulgp2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlRZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwiVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskVlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83HmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwYdgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cwta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2TN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPvt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDUAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISLDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnKxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEAmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfzet6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhrVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicDTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cncdn/RsYEONbwQSjIfMPkvxF+8HQ==" public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB" storage: - use-s3: true - endpoint: "http://localhost:8082/test-hideout" - public-url: "http://localhost:8082/test-hideout" - bucket: "test-hideout" - region: "auto" - access-key: "" - secret-key: "" + type: local spring: flyway: diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/AwsConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/AwsConfig.kt index 45db658d..ad40a0bd 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/AwsConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/AwsConfig.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.application.config +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import software.amazon.awssdk.auth.credentials.AwsBasicCredentials @@ -10,6 +11,7 @@ import java.net.URI @Configuration class AwsConfig { @Bean + @ConditionalOnProperty("hideout.storage.type", havingValue = "s3") fun s3Client(awsConfig: S3StorageConfig): S3Client { return S3Client.builder() .endpointOverride(URI.create(awsConfig.endpoint)) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt index 9dfe2f5e..f3e088ab 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.application.config import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -13,9 +14,6 @@ class SpringConfig { @Autowired lateinit var config: ApplicationConfig - @Autowired - lateinit var s3StorageConfig: S3StorageConfig - @Bean fun requestLoggingFilter(): CommonsRequestLoggingFilter { val loggingFilter = CommonsRequestLoggingFilter() @@ -34,6 +32,7 @@ data class ApplicationConfig( ) @ConfigurationProperties("hideout.storage.s3") +@ConditionalOnProperty("hideout.storage.type", havingValue = "s3") data class S3StorageConfig( val endpoint: String, val publicUrl: String, @@ -51,8 +50,9 @@ data class S3StorageConfig( * @property publicUrl 公開用URL 省略可能 指定するとHideoutがファイルを配信しなくなります。 */ @ConfigurationProperties("hideout.storage.local") +@ConditionalOnProperty("hideout.storage.type", havingValue = "local", matchIfMissing = true) data class LocalStorageConfig( - val path: String, + val path: String = "files", val publicUrl: String? ) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt index 420e82bf..a7300081 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt @@ -3,6 +3,7 @@ package dev.usbharu.hideout.core.service.media import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.LocalStorageConfig import org.slf4j.LoggerFactory +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.stereotype.Service import java.nio.file.Path import java.nio.file.StandardOpenOption @@ -12,6 +13,7 @@ import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream @Service +@ConditionalOnProperty("hideout.storage.type", havingValue = "local", matchIfMissing = true) /** * ローカルファイルシステムにメディアを保存します * diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/S3MediaDataStore.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/S3MediaDataStore.kt index 4220162c..8b0f2235 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/S3MediaDataStore.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/S3MediaDataStore.kt @@ -6,6 +6,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.withContext import org.slf4j.LoggerFactory +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.stereotype.Service import software.amazon.awssdk.core.sync.RequestBody import software.amazon.awssdk.services.s3.S3Client @@ -14,6 +15,7 @@ import software.amazon.awssdk.services.s3.model.GetUrlRequest import software.amazon.awssdk.services.s3.model.PutObjectRequest @Service +@ConditionalOnProperty("hideout.storage.type", havingValue = "s3") class S3MediaDataStore(private val s3Client: S3Client, private val s3StorageConfig: S3StorageConfig) : MediaDataStore { override suspend fun save(dataMediaSave: MediaSave): SavedMedia { val fileUploadRequest = PutObjectRequest.builder()