This commit is contained in:
かっこかり 2024-11-09 14:17:25 +09:00 committed by GitHub
commit 64b2c560f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 91 additions and 12 deletions
CHANGELOG.md
packages/backend/src
core/activitypub
misc/prelude
models
server/api/endpoints/i

View file

@ -30,6 +30,7 @@
- Enhance: 起動前の疎通チェックで、DBとメイン以外のRedisの疎通確認も行うように
(Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/588)
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/715)
- Enhance: 連合する必要のないプロフィール項目しか更新されなかった場合には連合先にUpdateアクティビティを発行しないように
- fix(backend): フォロワーへのメッセージの絵文字をemojisに含めるように
- Fix: Nested proxy requestsを検出した際にブロックするように
[ghsa-gq5q-c77c-v236](https://github.com/misskey-dev/misskey/security/advisories/ghsa-gq5q-c77c-v236)

View file

@ -9,7 +9,8 @@ import { In } from 'typeorm';
import * as mfm from 'mfm-js';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import type { MiPartialLocalUser, MiLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js';
import type { MiPartialLocalUser, MiLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser, MiLocalUserForApPersonRender } from '@/models/User.js';
import type { MiUserProfileForApPersonRender } from '@/models/UserProfile.js';
import type { IMentionedRemoteUsers, MiNote } from '@/models/Note.js';
import type { MiBlocking } from '@/models/Blocking.js';
import type { MiRelay } from '@/models/Relay.js';
@ -251,7 +252,7 @@ export class ApRendererService {
}
@bindThis
public renderKey(user: MiLocalUser, key: MiUserKeypair, postfix?: string): IKey {
public renderKey(user: { id: MiUser['id'] }, key: MiUserKeypair, postfix?: string): IKey {
return {
id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`,
type: 'Key',
@ -449,14 +450,25 @@ export class ApRendererService {
}
@bindThis
public async renderPerson(user: MiLocalUser) {
public async renderPerson(user: MiLocalUserForApPersonRender) {
const id = this.userEntityService.genLocalUserUri(user.id);
const isSystem = user.username.includes('.');
/**
* profile
*
* i/updateで虚無を連合するのを防止するための処理に伴い
* 使使
* miUserProfileKeysUsedForApPersonRenderを変更するのを
* asを使っている
*
* See https://github.com/misskey-dev/misskey/pull/14301
*/
const [avatar, banner, profile] = await Promise.all([
user.avatarId ? this.driveFilesRepository.findOneBy({ id: user.avatarId }) : undefined,
user.bannerId ? this.driveFilesRepository.findOneBy({ id: user.bannerId }) : undefined,
this.userProfilesRepository.findOneByOrFail({ userId: user.id }),
(this.userProfilesRepository.findOneByOrFail({ userId: user.id }) as Promise<MiUserProfileForApPersonRender>),
]);
const attachment = profile.fields.map(field => ({

View file

@ -0,0 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export function getObjKeys<T extends { [x: string]: unknown }>(obj: T): (keyof T)[] {
return Object.keys(obj);
}

View file

@ -307,6 +307,24 @@ export type MiPartialRemoteUser = Partial<MiUser> & {
uri: string;
}
export const miLocalUserKeysUsedForApPersonRender = [
'id',
'username',
'avatarId',
'bannerId',
'emojis',
'tags',
'isBot',
'isCat',
'name',
'isLocked',
'isExplorable',
'movedToUri',
'alsoKnownAs',
] as const satisfies (keyof MiLocalUser)[];
export type MiLocalUserForApPersonRender = Pick<MiLocalUser, typeof miLocalUserKeysUsedForApPersonRender[number]>;
export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
export const passwordSchema = { type: 'string', minLength: 1 } as const;
export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;

View file

@ -295,3 +295,12 @@ export class MiUserProfile {
}
}
}
export const miUserProfileKeysUsedForApPersonRender = [
'fields',
'description',
'birthday',
'location',
] as const satisfies (keyof MiUserProfile)[];
export type MiUserProfileForApPersonRender = Pick<MiUserProfile, typeof miUserProfileKeysUsedForApPersonRender[number]>;

View file

@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { isDeepStrictEqual } from 'node:util';
import RE2 from 're2';
import * as mfm from 'mfm-js';
import { Inject, Injectable } from '@nestjs/common';
@ -11,6 +12,7 @@ import { JSDOM } from 'jsdom';
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
import { extractHashtags } from '@/misc/extract-hashtags.js';
import * as Acct from '@/misc/acct.js';
import { getObjKeys } from '@/misc/prelude/object.js';
import type { UsersRepository, DriveFilesRepository, MiMeta, UserProfilesRepository, PagesRepository } from '@/models/_.js';
import type { MiLocalUser, MiUser } from '@/models/User.js';
import { birthdaySchema, descriptionSchema, followedMessageSchema, locationSchema, nameSchema } from '@/models/User.js';
@ -34,6 +36,8 @@ import type { Config } from '@/config.js';
import { safeForSql } from '@/misc/safe-for-sql.js';
import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
import { notificationRecieveConfig } from '@/models/json-schema/user.js';
import { miLocalUserKeysUsedForApPersonRender } from '@/models/User.js';
import { miUserProfileKeysUsedForApPersonRender } from '@/models/UserProfile.js';
import { ApiLoggerService } from '../../ApiLoggerService.js';
import { ApiError } from '../../error.js';
@ -507,15 +511,37 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
this.hashtagService.updateUsertags(user, tags);
//#endregion
if (Object.keys(updates).length > 0) {
await this.usersRepository.update(user.id, updates);
//#region 変更されていないプロパティを削除
const _updates = getObjKeys(updates).reduce<Partial<MiUser>>((acc, key) => {
if (updates[key] !== undefined && !isDeepStrictEqual(updates[key], user[key])) {
(acc[key] as MiUser[typeof key]) = updates[key];
}
return acc;
}, {});
const _profileUpdates = getObjKeys(profileUpdates).reduce<Partial<MiUserProfile>>((acc, key) => {
if (profileUpdates[key] !== undefined && !isDeepStrictEqual(profileUpdates[key], profile[key])) {
(acc[key] as MiUserProfile[typeof key]) = profileUpdates[key];
}
return acc;
}, {});
//#endregion
if (Object.keys(_updates).length > 0) {
await this.usersRepository.update(user.id, _updates);
this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id });
}
await this.userProfilesRepository.update(user.id, {
...profileUpdates,
verifiedLinks: [],
});
if (
Object.keys(_profileUpdates).length > 0 ||
profile.fields.filter(x => x.value.startsWith('https://')).length > 0 ||
profile.verifiedLinks.length > 0
) {
await this.userProfilesRepository.update(user.id, {
..._profileUpdates,
verifiedLinks: [],
});
}
const iObj = await this.userEntityService.pack(user.id, user, {
schema: 'MeDetailed',
@ -534,8 +560,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
this.userFollowingService.acceptAllFollowRequests(user);
}
// フォロワーにUpdateを配信
this.accountUpdateService.publishToFollowers(user.id);
// 連合する必要があるプロパティが変更されている場合はフォロワーにUpdateを配信
if (
miLocalUserKeysUsedForApPersonRender.some(k => getObjKeys(_updates).includes(k)) ||
miUserProfileKeysUsedForApPersonRender.some(k => getObjKeys(_profileUpdates).includes(k))
) {
this.accountUpdateService.publishToFollowers(user.id);
}
const urls = updatedProfile.fields.filter(x => x.value.startsWith('https://'));
for (const url of urls) {