From f88430aebc1e5a0151375fe50458b099af18196f Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Sun, 2 Mar 2025 21:08:05 -0500 Subject: [PATCH] add IObjectWithId type for APIs that work with objects required to have an ID. --- .../backend/src/core/HttpRequestService.ts | 6 ++--- .../src/core/activitypub/ApRequestService.ts | 6 ++--- .../src/core/activitypub/ApResolverService.ts | 24 ++++++++++--------- packages/backend/src/core/activitypub/type.ts | 4 ++++ 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index 19992a7597..1aa62a9879 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/HttpRequestService.ts @@ -16,7 +16,7 @@ import type { Config } from '@/config.js'; import { StatusError } from '@/misc/status-error.js'; import { bindThis } from '@/decorators.js'; import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js'; -import { IObject } from '@/core/activitypub/type.js'; +import type { IObject, IObjectWithId } from '@/core/activitypub/type.js'; import { ApUtilityService } from './activitypub/ApUtilityService.js'; import type { Response } from 'node-fetch'; import type { URL } from 'node:url'; @@ -217,7 +217,7 @@ export class HttpRequestService { } @bindThis - public async getActivityJson(url: string, isLocalAddressAllowed = false): Promise { + public async getActivityJson(url: string, isLocalAddressAllowed = false): Promise { const res = await this.send(url, { method: 'GET', headers: { @@ -237,7 +237,7 @@ export class HttpRequestService { // The caller (ApResolverService) will verify the ID against the original / entry URL, which ensures that all three match. this.apUtilityService.assertIdMatchesUrlAuthority(activity, res.url); - return activity; + return activity as IObjectWithId; } @bindThis diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts index b63d4eb2ab..952d1f5219 100644 --- a/packages/backend/src/core/activitypub/ApRequestService.ts +++ b/packages/backend/src/core/activitypub/ApRequestService.ts @@ -17,7 +17,7 @@ import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import type Logger from '@/logger.js'; import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js'; -import type { IObject } from './type.js'; +import type { IObject, IObjectWithId } from './type.js'; type Request = { url: string; @@ -185,7 +185,7 @@ export class ApRequestService { * @param followAlternate */ @bindThis - public async signedGet(url: string, user: { id: MiUser['id'] }, followAlternate?: boolean): Promise { + public async signedGet(url: string, user: { id: MiUser['id'] }, followAlternate?: boolean): Promise { const _followAlternate = followAlternate ?? true; const keypair = await this.userKeypairService.getUserKeypair(user.id); @@ -273,6 +273,6 @@ export class ApRequestService { // The caller (ApResolverService) will verify the ID against the original / entry URL, which ensures that all three match. this.apUtilityService.assertIdMatchesUrlAuthority(activity, res.url); - return activity; + return activity as IObjectWithId; } } diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index c5ac8a944d..12c3202af1 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -19,7 +19,7 @@ import { fromTuple } from '@/misc/from-tuple.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { ApLogService, calculateDurationSince, extractObjectContext } from '@/core/ApLogService.js'; import { ApUtilityService } from '@/core/activitypub/ApUtilityService.js'; -import { getApId, getNullableApId, isCollectionOrOrderedCollection } from './type.js'; +import { getApId, getNullableApId, IObjectWithId, isCollectionOrOrderedCollection } from './type.js'; import { ApDbResolverService } from './ApDbResolverService.js'; import { ApRendererService } from './ApRendererService.js'; import { ApRequestService } from './ApRequestService.js'; @@ -82,7 +82,7 @@ export class Resolver { * In all other cases, the object is re-fetched from remote by input string or object ID. */ @bindThis - public async secureResolve(input: ApObject, sentFromUri: string): Promise { + public async secureResolve(input: ApObject, sentFromUri: string): Promise { // Unpack arrays to get the value element. const value = fromTuple(input); if (value == null) { @@ -96,13 +96,15 @@ export class Resolver { // Our security requires that the object ID matches the host authority that sent it, otherwise it can't be trusted. // A mismatch isn't necessarily malicious, it just means we can't use the object we were given. if (typeof(value) === 'object' && this.apUtilityService.haveSameAuthority(id, sentFromUri)) { - return value; + return value as IObjectWithId; } // If the checks didn't pass, then we must fetch the object and use that. return await this.resolve(id); } + public async resolve(value: string | [string]): Promise; + public async resolve(value: string | IObject | [string | IObject]): Promise; @bindThis public async resolve(value: string | IObject | [string | IObject]): Promise { // eslint-disable-next-line no-param-reassign @@ -120,7 +122,7 @@ export class Resolver { } } - private async _resolveLogged(requestUri: string, host: string): Promise { + private async _resolveLogged(requestUri: string, host: string): Promise { const startTime = process.hrtime.bigint(); const log = await this.apLogService.createFetchLog({ @@ -149,7 +151,7 @@ export class Resolver { } } - private async _resolve(value: string, host: string, log?: SkApFetchLog): Promise { + private async _resolve(value: string, host: string, log?: SkApFetchLog): Promise { if (value.includes('#')) { // URLs with fragment parts cannot be resolved correctly because // the fragment part does not get transmitted over HTTP(S). @@ -168,7 +170,7 @@ export class Resolver { this.history.add(value); if (this.utilityService.isSelfHost(host)) { - return await this.resolveLocal(value); + return await this.resolveLocal(value) as IObjectWithId; } if (!this.utilityService.isFederationAllowedHost(host)) { @@ -180,8 +182,8 @@ export class Resolver { } const object = (this.user - ? await this.apRequestService.signedGet(value, this.user) as IObject - : await this.httpRequestService.getActivityJson(value)) as IObject; + ? await this.apRequestService.signedGet(value, this.user) + : await this.httpRequestService.getActivityJson(value)); if (log) { const { object: objectOnly, context, contextHash } = extractObjectContext(object); @@ -226,7 +228,7 @@ export class Resolver { } @bindThis - private resolveLocal(url: string): Promise { + private resolveLocal(url: string): Promise { const parsed = this.apDbResolverService.parseUri(url); if (!parsed.local) throw new IdentifiableError('02b40cd0-fa92-4b0c-acc9-fb2ada952ab8', `resolveLocal - not a local URL: ${url}`); @@ -241,7 +243,7 @@ export class Resolver { } else { return this.apRendererService.renderNote(note, author); } - }); + }) as Promise; case 'users': return this.usersRepository.findOneByOrFail({ id: parsed.id }) .then(user => this.apRendererService.renderPerson(user as MiLocalUser)); @@ -251,7 +253,7 @@ export class Resolver { this.notesRepository.findOneByOrFail({ id: parsed.id }), this.pollsRepository.findOneByOrFail({ noteId: parsed.id }), ]) - .then(([note, poll]) => this.apRendererService.renderQuestion({ id: note.userId }, note, poll)); + .then(([note, poll]) => this.apRendererService.renderQuestion({ id: note.userId }, note, poll)) as Promise; case 'likes': return this.noteReactionsRepository.findOneByOrFail({ id: parsed.id }).then(async reaction => this.apRendererService.addContext(await this.apRendererService.renderLike(reaction, { uri: null }))); diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts index a319c0b6ea..e7459a57d2 100644 --- a/packages/backend/src/core/activitypub/type.ts +++ b/packages/backend/src/core/activitypub/type.ts @@ -39,6 +39,10 @@ export interface IObject { sensitive?: boolean; } +export interface IObjectWithId extends IObject { + id: string; +} + /** * Get array of ActivityStreams Objects id */