fetchPersonWithRenewal
This commit is contained in:
		
							parent
							
								
									a5cccf3799
								
							
						
					
					
						commit
						54fe8ca600
					
				|  | @ -40,6 +40,7 @@ type NodeInfo = { | |||
| @Injectable() | ||||
| export class FetchInstanceMetadataService { | ||||
| 	private logger: Logger; | ||||
| 	private httpColon = 'https://'; | ||||
| 
 | ||||
| 	constructor( | ||||
| 		private httpRequestService: HttpRequestService, | ||||
|  | @ -49,6 +50,7 @@ export class FetchInstanceMetadataService { | |||
| 		private redisClient: Redis.Redis, | ||||
| 	) { | ||||
| 		this.logger = this.loggerService.getLogger('metadata', 'cyan'); | ||||
| 		this.httpColon = 'http://'; | ||||
| 	} | ||||
| 
 | ||||
| 	@bindThis | ||||
|  | @ -72,8 +74,7 @@ export class FetchInstanceMetadataService { | |||
| 				const _instance = await this.federatedInstanceService.fetch(host); | ||||
| 				const now = Date.now(); | ||||
| 				if (_instance && _instance.infoUpdatedAt && (now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 3)) { | ||||
| 					// unlock at the finally caluse
 | ||||
| 					return; | ||||
| 					throw new Error('Skip because updated recently'); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
|  | @ -119,6 +120,12 @@ export class FetchInstanceMetadataService { | |||
| 			await this.federatedInstanceService.update(instance.id, updates); | ||||
| 
 | ||||
| 			this.logger.succ(`Successfuly updated metadata of ${instance.host}`); | ||||
| 			this.logger.debug('Updated metadata:', { | ||||
| 				info: !!info, | ||||
| 				dom: !!dom, | ||||
| 				manifest: !!manifest, | ||||
| 				updates, | ||||
| 			}); | ||||
| 		} catch (e) { | ||||
| 			this.logger.error(`Failed to update metadata of ${instance.host}: ${e}`); | ||||
| 		} finally { | ||||
|  | @ -131,7 +138,7 @@ export class FetchInstanceMetadataService { | |||
| 		this.logger.info(`Fetching nodeinfo of ${instance.host} ...`); | ||||
| 
 | ||||
| 		try { | ||||
| 			const wellknown = await this.httpRequestService.getJson('https://' + instance.host + '/.well-known/nodeinfo') | ||||
| 			const wellknown = await this.httpRequestService.getJson(this.httpColon + instance.host + '/.well-known/nodeinfo') | ||||
| 				.catch(err => { | ||||
| 					if (err.statusCode === 404) { | ||||
| 						throw new Error('No nodeinfo provided'); | ||||
|  | @ -174,7 +181,7 @@ export class FetchInstanceMetadataService { | |||
| 	private async fetchDom(instance: MiInstance): Promise<DOMWindow['document']> { | ||||
| 		this.logger.info(`Fetching HTML of ${instance.host} ...`); | ||||
| 
 | ||||
| 		const url = 'https://' + instance.host; | ||||
| 		const url = this.httpColon + instance.host; | ||||
| 
 | ||||
| 		const html = await this.httpRequestService.getHtml(url); | ||||
| 
 | ||||
|  | @ -186,7 +193,7 @@ export class FetchInstanceMetadataService { | |||
| 
 | ||||
| 	@bindThis | ||||
| 	private async fetchManifest(instance: MiInstance): Promise<Record<string, unknown> | null> { | ||||
| 		const url = 'https://' + instance.host; | ||||
| 		const url = this.httpColon + instance.host; | ||||
| 
 | ||||
| 		const manifestUrl = url + '/manifest.json'; | ||||
| 
 | ||||
|  | @ -197,7 +204,7 @@ export class FetchInstanceMetadataService { | |||
| 
 | ||||
| 	@bindThis | ||||
| 	private async fetchFaviconUrl(instance: MiInstance, doc: DOMWindow['document'] | null): Promise<string | null> { | ||||
| 		const url = 'https://' + instance.host; | ||||
| 		const url = this.httpColon + instance.host; | ||||
| 
 | ||||
| 		if (doc) { | ||||
| 			// https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043
 | ||||
|  | @ -224,12 +231,12 @@ export class FetchInstanceMetadataService { | |||
| 	@bindThis | ||||
| 	private async fetchIconUrl(instance: MiInstance, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> { | ||||
| 		if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) { | ||||
| 			const url = 'https://' + instance.host; | ||||
| 			const url = this.httpColon + instance.host; | ||||
| 			return (new URL(manifest.icons[0].src, url)).href; | ||||
| 		} | ||||
| 
 | ||||
| 		if (doc) { | ||||
| 			const url = 'https://' + instance.host; | ||||
| 			const url = this.httpColon + instance.host; | ||||
| 
 | ||||
| 			// https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043
 | ||||
| 			const links = Array.from(doc.getElementsByTagName('link')).reverse(); | ||||
|  |  | |||
|  | @ -122,7 +122,7 @@ export class ApDbResolverService implements OnApplicationShutdown { | |||
| 		user: MiRemoteUser; | ||||
| 		key: MiUserPublickey | null; | ||||
| 	} | null> { | ||||
| 		const user = await this.apPersonService.resolvePerson(uri) as MiRemoteUser; | ||||
| 		const user = await this.apPersonService.resolvePerson(uri, undefined, true) as MiRemoteUser; | ||||
| 		if (user.isDeleted) return null; | ||||
| 
 | ||||
| 		const keys = await this.publicKeyByUserIdCache.fetch( | ||||
|  |  | |||
|  | @ -36,7 +36,6 @@ import { ApResolverService } from './ApResolverService.js'; | |||
| import { ApAudienceService } from './ApAudienceService.js'; | ||||
| import { ApPersonService } from './models/ApPersonService.js'; | ||||
| import { ApQuestionService } from './models/ApQuestionService.js'; | ||||
| import { CacheService } from '@/core/CacheService.js'; | ||||
| import { GlobalEventService } from '@/core/GlobalEventService.js'; | ||||
| import type { Resolver } from './ApResolverService.js'; | ||||
| import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove } from './type.js'; | ||||
|  | @ -109,15 +108,6 @@ export class ApInboxService { | |||
| 		} else { | ||||
| 			await this.performOneActivity(actor, activity); | ||||
| 		} | ||||
| 
 | ||||
| 		// ついでにリモートユーザーの情報が古かったら更新しておく
 | ||||
| 		if (actor.uri) { | ||||
| 			if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) { | ||||
| 				setImmediate(() => { | ||||
| 					this.apPersonService.updatePerson(actor.uri); | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@bindThis | ||||
|  |  | |||
|  | @ -107,16 +107,24 @@ export class ApRequestService { | |||
| 	@bindThis | ||||
| 	public async signedPost(user: { id: MiUser['id'] }, url: string, object: unknown, level: string): Promise<void> { | ||||
| 		const body = typeof object === 'string' ? object : JSON.stringify(object); | ||||
| 
 | ||||
| 		const key = await this.getPrivateKey(user.id, level); | ||||
| 		const req = createSignedPost({ | ||||
| 			level, | ||||
| 			key: await this.getPrivateKey(user.id, level), | ||||
| 			key, | ||||
| 			url, | ||||
| 			body, | ||||
| 			additionalHeaders: { | ||||
| 				'User-Agent': this.config.userAgent, | ||||
| 			}, | ||||
| 		}); | ||||
| 
 | ||||
| 		this.logger.debug('create signed post', { | ||||
| 			version: 'draft', | ||||
| 			level, | ||||
| 			url, | ||||
| 			keyId: key.keyId, | ||||
| 		}); | ||||
| 
 | ||||
| 		await this.httpRequestService.send(url, { | ||||
| 			method: req.request.method, | ||||
| 			headers: req.request.headers, | ||||
|  | @ -131,14 +139,23 @@ export class ApRequestService { | |||
| 	 */ | ||||
| 	@bindThis | ||||
| 	public async signedGet(url: string, user: { id: MiUser['id'] }, level: string): Promise<unknown> { | ||||
| 		const key = await this.getPrivateKey(user.id, level); | ||||
| 		const req = createSignedGet({ | ||||
| 			level, | ||||
| 			key: await this.getPrivateKey(user.id, level), | ||||
| 			key, | ||||
| 			url, | ||||
| 			additionalHeaders: { | ||||
| 				'User-Agent': this.config.userAgent, | ||||
| 			}, | ||||
| 		}); | ||||
| 
 | ||||
| 		this.logger.debug('create signed get', { | ||||
| 			version: 'draft', | ||||
| 			level, | ||||
| 			url, | ||||
| 			keyId: key.keyId, | ||||
| 		}); | ||||
| 
 | ||||
| 		const res = await this.httpRequestService.send(url, { | ||||
| 			method: req.request.method, | ||||
| 			headers: req.request.headers, | ||||
|  |  | |||
|  | @ -248,6 +248,22 @@ export class ApPersonService implements OnModuleInit { | |||
| 		return null; | ||||
| 	} | ||||
| 
 | ||||
| 	@bindThis | ||||
| 	async fetchPersonWithRenewal(uri: string): Promise<MiLocalUser | MiRemoteUser | null> { | ||||
| 		const exist = await this.fetchPerson(uri); | ||||
| 		if (exist == null) return null; | ||||
| 
 | ||||
| 		// ついでにリモートユーザーの情報が古かったら更新しておく
 | ||||
| 		if (this.userEntityService.isRemoteUser(exist)) { | ||||
| 			if (exist.lastFetchedAt == null || Date.now() - exist.lastFetchedAt.getTime() > 1000 * 60 * 60 * 3) { | ||||
| 				await this.updatePerson(exist.uri); | ||||
| 				return await this.fetchPerson(uri); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return exist; | ||||
| 	} | ||||
| 
 | ||||
| 	private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Partial<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>>> { | ||||
| 		if (user == null) throw new Error('failed to create user: user is null'); | ||||
| 
 | ||||
|  | @ -624,9 +640,9 @@ export class ApPersonService implements OnModuleInit { | |||
| 	 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 | ||||
| 	 */ | ||||
| 	@bindThis | ||||
| 	public async resolvePerson(uri: string, resolver?: Resolver): Promise<MiLocalUser | MiRemoteUser> { | ||||
| 	public async resolvePerson(uri: string, resolver?: Resolver, withRenewal = false): Promise<MiLocalUser | MiRemoteUser> { | ||||
| 		//#region このサーバーに既に登録されていたらそれを返す
 | ||||
| 		const exist = await this.fetchPerson(uri); | ||||
| 		const exist = withRenewal ? await this.fetchPersonWithRenewal(uri) : await this.fetchPerson(uri); | ||||
| 		if (exist) return exist; | ||||
| 		//#endregion
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ import type { MiRemoteUser } from '@/models/User.js'; | |||
| import type { MiUserPublickey } from '@/models/UserPublickey.js'; | ||||
| import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js'; | ||||
| import { StatusError } from '@/misc/status-error.js'; | ||||
| import * as Acct from '@/misc/acct.js'; | ||||
| import { UtilityService } from '@/core/UtilityService.js'; | ||||
| import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; | ||||
| import { LdSignatureService } from '@/core/activitypub/LdSignatureService.js'; | ||||
|  | @ -79,7 +80,6 @@ export class InboxProcessorService { | |||
| 			key: MiUserPublickey | null; | ||||
| 		} | null = null; | ||||
| 
 | ||||
| 		// keyIdでわからなければ、activity.actorを元にDBから取得 || activity.actorを元にリモートから取得
 | ||||
| 		try { | ||||
| 			authUser = await this.apDbResolverService.getAuthUserFromApId(getApId(activity.actor), signature.keyId); | ||||
| 		} catch (err) { | ||||
|  | @ -103,7 +103,15 @@ export class InboxProcessorService { | |||
| 		} | ||||
| 
 | ||||
| 		// HTTP-Signatureの検証
 | ||||
| 		const httpSignatureValidated = verifyDraftSignature(signature, authUser.key.keyPem); | ||||
| 		const errorLogger = (ms: any) => this.logger.error(ms); | ||||
| 		const httpSignatureValidated = verifyDraftSignature(signature, authUser.key.keyPem, errorLogger); | ||||
| 		this.logger.debug('Inbox message validation: ', { | ||||
| 			userId: authUser.user.id, | ||||
| 			userAcct: Acct.toString(authUser.user), | ||||
| 			parsedKeyId: signature.keyId, | ||||
| 			foundKeyId: authUser.key.keyId, | ||||
| 			httpSignatureValidated, | ||||
| 		}); | ||||
| 
 | ||||
| 		// また、signatureのsignerは、activity.actorと一致する必要がある
 | ||||
| 		if (!httpSignatureValidated || authUser.user.uri !== activity.actor) { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue