From 795fc5e7bca45aca90815cdaa9404cdb1fd73eb6 Mon Sep 17 00:00:00 2001 From: mei23 Date: Sat, 1 Sep 2018 16:39:46 +0900 Subject: [PATCH 1/5] Set Person.updatedAt at first --- src/remote/activitypub/models/person.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index 3bd4e16763..212a1c5ee6 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -139,6 +139,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise Date: Sat, 1 Sep 2018 16:46:41 +0900 Subject: [PATCH 2/5] Add missing updatePerson properties --- src/remote/activitypub/models/person.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index 212a1c5ee6..0246798616 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -291,7 +291,9 @@ export async function updatePerson(uri: string, resolver?: Resolver): Promise Date: Sat, 1 Sep 2018 16:55:11 +0900 Subject: [PATCH 3/5] =?UTF-8?q?updatePerson=E3=81=A7=E5=86=8D=E5=89=B2?= =?UTF-8?q?=E3=82=8A=E5=BD=93=E3=81=A6=E3=82=92=E8=80=83=E6=85=AE=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/remote/activitypub/models/person.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index 0246798616..9c770141a3 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -293,7 +293,12 @@ export async function updatePerson(uri: string, resolver?: Resolver): Promise Date: Sat, 1 Sep 2018 17:53:38 +0900 Subject: [PATCH 4/5] Receive Update activity --- src/queue/processors/http/process-inbox.ts | 28 +++++++++++++++------- src/remote/activitypub/models/person.ts | 8 ++++--- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/queue/processors/http/process-inbox.ts b/src/queue/processors/http/process-inbox.ts index 7e564dd32a..8e6b3769de 100644 --- a/src/queue/processors/http/process-inbox.ts +++ b/src/queue/processors/http/process-inbox.ts @@ -5,7 +5,7 @@ const httpSignature = require('http-signature'); import parseAcct from '../../../misc/acct/parse'; import User, { IRemoteUser } from '../../../models/user'; import perform from '../../../remote/activitypub/perform'; -import { resolvePerson } from '../../../remote/activitypub/models/person'; +import { resolvePerson, updatePerson } from '../../../remote/activitypub/models/person'; import { toUnicode } from 'punycode'; import { URL } from 'url'; @@ -44,11 +44,6 @@ export default async (job: bq.Job, done: any): Promise => { } user = await User.findOne({ usernameLower: username, host: host.toLowerCase() }) as IRemoteUser; - - // アクティビティを送信してきたユーザーがまだMisskeyサーバーに登録されていなかったら登録する - if (user === null) { - user = await resolvePerson(activity.actor) as IRemoteUser; - } } else { // アクティビティ内のホストの検証 const host = toUnicode(new URL(signature.keyId).hostname.toLowerCase()); @@ -64,11 +59,26 @@ export default async (job: bq.Job, done: any): Promise => { host: { $ne: null }, 'publicKey.id': signature.keyId }) as IRemoteUser; + } - // アクティビティを送信してきたユーザーがまだMisskeyサーバーに登録されていなかったら登録する - if (user === null) { - user = await resolvePerson(activity.actor) as IRemoteUser; + // Update activityの場合は、ここで署名検証/更新処理まで実施して終了 + if (activity.type === 'Update') { + if (activity.object && activity.object.type === 'Person') { + if (user == null) { + console.warn('Update activity received, but user not registed.'); + } else if (!httpSignature.verifySignature(signature, user.publicKey.publicKeyPem)) { + console.warn('Update activity received, but signature verification failed.'); + } else { + updatePerson(activity.actor, null, activity.object); + } } + done(); + return; + } + + // アクティビティを送信してきたユーザーがまだMisskeyサーバーに登録されていなかったら登録する + if (user === null) { + user = await resolvePerson(activity.actor) as IRemoteUser; } if (user === null) { diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index 9c770141a3..dff38f5460 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -216,10 +216,12 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise { +export async function updatePerson(uri: string, resolver?: Resolver, hint?: object): Promise { if (typeof uri !== 'string') throw 'uri is not string'; // URIがこのサーバーを指しているならスキップ @@ -237,7 +239,7 @@ export async function updatePerson(uri: string, resolver?: Resolver): Promise Date: Sat, 1 Sep 2018 20:17:30 +0900 Subject: [PATCH 5/5] Send Update activity --- src/remote/activitypub/renderer/update.ts | 14 +++++++++ src/server/api/endpoints/i/update.ts | 4 +++ src/services/i/update.ts | 38 +++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 src/remote/activitypub/renderer/update.ts create mode 100644 src/services/i/update.ts diff --git a/src/remote/activitypub/renderer/update.ts b/src/remote/activitypub/renderer/update.ts new file mode 100644 index 0000000000..cf9acc9acb --- /dev/null +++ b/src/remote/activitypub/renderer/update.ts @@ -0,0 +1,14 @@ +import config from '../../../config'; +import { ILocalUser } from '../../../models/user'; + +export default (object: any, user: ILocalUser) => { + const activity = { + id: `${config.url}/users/${user._id}#updates/${new Date().getTime()}`, + actor: `${config.url}/users/${user._id}`, + type: 'Update', + to: [ 'https://www.w3.org/ns/activitystreams#Public' ], + object + } as any; + + return activity; +}; diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index cdb4eb3f56..585339e249 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -5,6 +5,7 @@ import DriveFile from '../../../../models/drive-file'; import acceptAllFollowRequests from '../../../../services/following/requests/accept-all'; import { IApp } from '../../../../models/app'; import config from '../../../../config'; +import { publishToFollowers } from '../../../../services/i/update'; export const meta = { desc: { @@ -144,4 +145,7 @@ export default async (params: any, user: ILocalUser, app: IApp) => new Promise(a if (user.isLocked && isLocked === false) { acceptAllFollowRequests(user); } + + // フォロワーにUpdateを配信 + publishToFollowers(user._id); }); diff --git a/src/services/i/update.ts b/src/services/i/update.ts new file mode 100644 index 0000000000..25b55b0355 --- /dev/null +++ b/src/services/i/update.ts @@ -0,0 +1,38 @@ +import * as mongo from 'mongodb'; +import User, { isLocalUser, isRemoteUser } from '../../models/user'; +import Following from '../../models/following'; +import renderPerson from '../../remote/activitypub/renderer/person'; +import renderUpdate from '../../remote/activitypub/renderer/update'; +import packAp from '../../remote/activitypub/renderer'; +import { deliver } from '../../queue'; + +export async function publishToFollowers(userId: mongo.ObjectID) { + const user = await User.findOne({ + _id: userId + }); + + const followers = await Following.find({ + followeeId: user._id + }); + + const queue: string[] = []; + + // フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信 + if (isLocalUser(user)) { + followers.map(following => { + const follower = following._follower; + + if (isRemoteUser(follower)) { + const inbox = follower.sharedInbox || follower.inbox; + if (!queue.includes(inbox)) queue.push(inbox); + } + }); + + if (queue.length > 0) { + const content = packAp(renderUpdate(await renderPerson(user), user)); + queue.forEach(inbox => { + deliver(user, content, inbox); + }); + } + } +}