Handle correctly formatted AP attributedTo

pull/5870/head
Chocobozzz 2023-06-05 16:18:57 +02:00
parent cefe22cf7c
commit 7f7e9d4e90
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
7 changed files with 41 additions and 28 deletions

View File

@ -51,7 +51,8 @@ function setValidAttributedTo (obj: any) {
} }
obj.attributedTo = obj.attributedTo.filter(a => { obj.attributedTo = obj.attributedTo.filter(a => {
return (a.type === 'Group' || a.type === 'Person') && isActivityPubUrlValid(a.id) return isActivityPubUrlValid(a) ||
((a.type === 'Group' || a.type === 'Person') && isActivityPubUrlValid(a.id))
}) })
return true return true

View File

@ -3,8 +3,9 @@ import { logger } from '@server/helpers/logger'
import { JobQueue } from '@server/lib/job-queue' import { JobQueue } from '@server/lib/job-queue'
import { ActorLoadByUrlType, loadActorByUrl } from '@server/lib/model-loaders' import { ActorLoadByUrlType, loadActorByUrl } from '@server/lib/model-loaders'
import { MActor, MActorAccountChannelId, MActorAccountChannelIdActor, MActorAccountId, MActorFullActor } from '@server/types/models' import { MActor, MActorAccountChannelId, MActorAccountChannelIdActor, MActorAccountId, MActorFullActor } from '@server/types/models'
import { ActivityPubActor } from '@shared/models' import { arrayify } from '@shared/core-utils'
import { getAPId } from '../activity' import { ActivityPubActor, APObjectId } from '@shared/models'
import { fetchAPObject, getAPId } from '../activity'
import { checkUrlsSameHost } from '../url' import { checkUrlsSameHost } from '../url'
import { refreshActorIfNeeded } from './refresh' import { refreshActorIfNeeded } from './refresh'
import { APActorCreator, fetchRemoteActor } from './shared' import { APActorCreator, fetchRemoteActor } from './shared'
@ -40,7 +41,7 @@ async function getOrCreateAPActor (
const { actorObject } = await fetchRemoteActor(actorUrl) const { actorObject } = await fetchRemoteActor(actorUrl)
if (actorObject === undefined) throw new Error('Cannot fetch remote actor ' + actorUrl) if (actorObject === undefined) throw new Error('Cannot fetch remote actor ' + actorUrl)
// actorUrl is just an alias/rediraction, so process object id instead // actorUrl is just an alias/redirection, so process object id instead
if (actorObject.id !== actorUrl) return getOrCreateAPActor(actorObject, 'all', recurseIfNeeded, updateCollections) if (actorObject.id !== actorUrl) return getOrCreateAPActor(actorObject, 'all', recurseIfNeeded, updateCollections)
// Create the attributed to actor // Create the attributed to actor
@ -68,29 +69,48 @@ async function getOrCreateAPActor (
return actorRefreshed return actorRefreshed
} }
function getOrCreateAPOwner (actorObject: ActivityPubActor, actorUrl: string) { async function getOrCreateAPOwner (actorObject: ActivityPubActor, actorUrl: string) {
const accountAttributedTo = actorObject.attributedTo.find(a => a.type === 'Person') const accountAttributedTo = await findOwner(actorUrl, actorObject.attributedTo, 'Person')
if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actorUrl) if (!accountAttributedTo) {
throw new Error(`Cannot find account attributed to video channel ${actorUrl}`)
if (checkUrlsSameHost(accountAttributedTo.id, actorUrl) !== true) {
throw new Error(`Account attributed to ${accountAttributedTo.id} does not have the same host than actor url ${actorUrl}`)
} }
try { try {
// Don't recurse another time // Don't recurse another time
const recurseIfNeeded = false const recurseIfNeeded = false
return getOrCreateAPActor(accountAttributedTo.id, 'all', recurseIfNeeded) return getOrCreateAPActor(accountAttributedTo, 'all', recurseIfNeeded)
} catch (err) { } catch (err) {
logger.error('Cannot get or create account attributed to video channel ' + actorUrl) logger.error('Cannot get or create account attributed to video channel ' + actorUrl)
throw new Error(err) throw new Error(err)
} }
} }
async function findOwner (rootUrl: string, attributedTo: APObjectId[] | APObjectId, type: 'Person' | 'Group') {
for (const actorToCheck of arrayify(attributedTo)) {
const actorObject = await fetchAPObject<ActivityPubActor>(getAPId(actorToCheck))
if (!actorObject) {
logger.warn('Unknown attributed to actor %s for owner %s', actorToCheck, rootUrl)
continue
}
if (checkUrlsSameHost(actorObject.id, rootUrl) !== true) {
logger.warn(`Account attributed to ${actorObject.id} does not have the same host than owner actor url ${rootUrl}`)
continue
}
if (actorObject.type === type) return actorObject
}
return undefined
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export { export {
getOrCreateAPOwner, getOrCreateAPOwner,
getOrCreateAPActor getOrCreateAPActor,
findOwner
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -77,7 +77,7 @@ async function setVideoChannel (playlistObject: PlaylistObject, playlistAttribut
throw new Error('Not attributed to for playlist object ' + getAPId(playlistObject)) throw new Error('Not attributed to for playlist object ' + getAPId(playlistObject))
} }
const actor = await getOrCreateAPActor(playlistObject.attributedTo[0], 'all') const actor = await getOrCreateAPActor(getAPId(playlistObject.attributedTo[0]), 'all')
if (!actor.VideoChannel) { if (!actor.VideoChannel) {
logger.warn('Playlist "attributedTo" %s is not a video channel.', playlistObject.id, { playlistObject, ...lTags(playlistObject.id) }) logger.warn('Playlist "attributedTo" %s is not a video channel.', playlistObject.id, { playlistObject, ...lTags(playlistObject.id) })

View File

@ -18,8 +18,7 @@ import {
MVideoThumbnail MVideoThumbnail
} from '@server/types/models' } from '@server/types/models'
import { ActivityTagObject, ThumbnailType, VideoObject, VideoStreamingPlaylistType } from '@shared/models' import { ActivityTagObject, ThumbnailType, VideoObject, VideoStreamingPlaylistType } from '@shared/models'
import { getOrCreateAPActor } from '../../actors' import { findOwner, getOrCreateAPActor } from '../../actors'
import { checkUrlsSameHost } from '../../url'
import { import {
getCaptionAttributesFromObject, getCaptionAttributesFromObject,
getFileAttributesFromUrl, getFileAttributesFromUrl,
@ -37,13 +36,9 @@ export abstract class APVideoAbstractBuilder {
protected abstract lTags: LoggerTagsFn protected abstract lTags: LoggerTagsFn
protected async getOrCreateVideoChannelFromVideoObject () { protected async getOrCreateVideoChannelFromVideoObject () {
const channel = this.videoObject.attributedTo.find(a => a.type === 'Group') const channel = await findOwner(this.videoObject.id, this.videoObject.attributedTo, 'Group')
if (!channel) throw new Error('Cannot find associated video channel to video ' + this.videoObject.url) if (!channel) throw new Error('Cannot find associated video channel to video ' + this.videoObject.url)
if (checkUrlsSameHost(channel.id, this.videoObject.id) !== true) {
throw new Error(`Video channel url ${channel.id} does not have the same host than video object id ${this.videoObject.id}`)
}
return getOrCreateAPActor(channel.id, 'all') return getOrCreateAPActor(channel.id, 'all')
} }

View File

@ -114,10 +114,7 @@ export type ActivityUrlObject =
| ActivityVideoFileMetadataUrlObject | ActivityVideoFileMetadataUrlObject
| ActivityTrackerUrlObject | ActivityTrackerUrlObject
export interface ActivityPubAttributedTo { export type ActivityPubAttributedTo = { type: 'Group' | 'Person', id: string } | string
type: 'Group' | 'Person'
id: string
}
export interface ActivityTombstoneObject { export interface ActivityTombstoneObject {
'@context'?: any '@context'?: any

View File

@ -1,4 +1,4 @@
import { ActivityIconObject } from './common-objects' import { ActivityIconObject, ActivityPubAttributedTo } from './common-objects'
export interface PlaylistObject { export interface PlaylistObject {
id: string id: string
@ -12,7 +12,7 @@ export interface PlaylistObject {
uuid: string uuid: string
totalItems: number totalItems: number
attributedTo: string[] attributedTo: ActivityPubAttributedTo[]
icon?: ActivityIconObject icon?: ActivityIconObject

View File

@ -1,4 +1,4 @@
import { ActivityTagObject } from './common-objects' import { ActivityPubAttributedTo, ActivityTagObject } from './common-objects'
export interface VideoCommentObject { export interface VideoCommentObject {
type: 'Note' type: 'Note'
@ -11,6 +11,6 @@ export interface VideoCommentObject {
published: string published: string
updated: string updated: string
url: string url: string
attributedTo: string attributedTo: ActivityPubAttributedTo
tag: ActivityTagObject[] tag: ActivityTagObject[]
} }