Add an option to sign federated fetches for mastodon compatibility (#5898)
* Fix player error modal Not hidden when we change the video * Correctly dispose player components * Sign cross-server fetch requests for mastodon AUTHORIZED_FETCH compatibilty * Add a remote fetch sign configuration knob * Federated fetches refactoring --------- Co-authored-by: Chocobozzz <me@florianbigard.com> Co-authored-by: ira <ira@foxgirl.space>
This commit is contained in:
parent
787d822cd4
commit
f862be2749
|
@ -374,6 +374,9 @@ plugins:
|
||||||
url: 'https://packages.joinpeertube.org'
|
url: 'https://packages.joinpeertube.org'
|
||||||
|
|
||||||
federation:
|
federation:
|
||||||
|
# Some federated software such as Mastodon may require an HTTP signature to access content
|
||||||
|
sign_federated_fetches: true
|
||||||
|
|
||||||
videos:
|
videos:
|
||||||
federate_unlisted: false
|
federate_unlisted: false
|
||||||
|
|
||||||
|
|
|
@ -372,6 +372,9 @@ plugins:
|
||||||
url: 'https://packages.joinpeertube.org'
|
url: 'https://packages.joinpeertube.org'
|
||||||
|
|
||||||
federation:
|
federation:
|
||||||
|
# Some federated software such as Mastodon may require an HTTP signature to access content
|
||||||
|
sign_federated_fetches: true
|
||||||
|
|
||||||
videos:
|
videos:
|
||||||
federate_unlisted: false
|
federate_unlisted: false
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { sanitizeUrl } from '@server/helpers/core-utils'
|
import { sanitizeUrl } from '@server/helpers/core-utils'
|
||||||
import { pickSearchChannelQuery } from '@server/helpers/query'
|
import { pickSearchChannelQuery } from '@server/helpers/query'
|
||||||
import { doJSONRequest, findLatestRedirection } from '@server/helpers/requests'
|
import { doJSONRequest } from '@server/helpers/requests'
|
||||||
import { CONFIG } from '@server/initializers/config'
|
import { CONFIG } from '@server/initializers/config'
|
||||||
import { WEBSERVER } from '@server/initializers/constants'
|
import { WEBSERVER } from '@server/initializers/constants'
|
||||||
|
import { findLatestAPRedirection } from '@server/lib/activitypub/activity'
|
||||||
import { Hooks } from '@server/lib/plugins/hooks'
|
import { Hooks } from '@server/lib/plugins/hooks'
|
||||||
import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
|
import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
|
||||||
import { getServerActor } from '@server/models/application/application'
|
import { getServerActor } from '@server/models/application/application'
|
||||||
|
@ -126,7 +127,7 @@ async function searchVideoChannelURI (search: string, res: express.Response) {
|
||||||
|
|
||||||
if (isUserAbleToSearchRemoteURI(res)) {
|
if (isUserAbleToSearchRemoteURI(res)) {
|
||||||
try {
|
try {
|
||||||
const latestUri = await findLatestRedirection(uri, { activityPub: true })
|
const latestUri = await findLatestAPRedirection(uri)
|
||||||
|
|
||||||
const actor = await getOrCreateAPActor(latestUri, 'all', true, true)
|
const actor = await getOrCreateAPActor(latestUri, 'all', true, true)
|
||||||
videoChannel = actor.VideoChannel
|
videoChannel = actor.VideoChannel
|
||||||
|
|
|
@ -3,10 +3,11 @@ import { sanitizeUrl } from '@server/helpers/core-utils'
|
||||||
import { isUserAbleToSearchRemoteURI } from '@server/helpers/express-utils'
|
import { isUserAbleToSearchRemoteURI } from '@server/helpers/express-utils'
|
||||||
import { logger } from '@server/helpers/logger'
|
import { logger } from '@server/helpers/logger'
|
||||||
import { pickSearchPlaylistQuery } from '@server/helpers/query'
|
import { pickSearchPlaylistQuery } from '@server/helpers/query'
|
||||||
import { doJSONRequest, findLatestRedirection } from '@server/helpers/requests'
|
import { doJSONRequest } from '@server/helpers/requests'
|
||||||
import { getFormattedObjects } from '@server/helpers/utils'
|
import { getFormattedObjects } from '@server/helpers/utils'
|
||||||
import { CONFIG } from '@server/initializers/config'
|
import { CONFIG } from '@server/initializers/config'
|
||||||
import { WEBSERVER } from '@server/initializers/constants'
|
import { WEBSERVER } from '@server/initializers/constants'
|
||||||
|
import { findLatestAPRedirection } from '@server/lib/activitypub/activity'
|
||||||
import { getOrCreateAPVideoPlaylist } from '@server/lib/activitypub/playlists/get'
|
import { getOrCreateAPVideoPlaylist } from '@server/lib/activitypub/playlists/get'
|
||||||
import { Hooks } from '@server/lib/plugins/hooks'
|
import { Hooks } from '@server/lib/plugins/hooks'
|
||||||
import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
|
import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
|
||||||
|
@ -105,7 +106,7 @@ async function searchVideoPlaylistsURI (search: string, res: express.Response) {
|
||||||
|
|
||||||
if (isUserAbleToSearchRemoteURI(res)) {
|
if (isUserAbleToSearchRemoteURI(res)) {
|
||||||
try {
|
try {
|
||||||
const url = await findLatestRedirection(search, { activityPub: true })
|
const url = await findLatestAPRedirection(search)
|
||||||
|
|
||||||
videoPlaylist = await getOrCreateAPVideoPlaylist(url)
|
videoPlaylist = await getOrCreateAPVideoPlaylist(url)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { sanitizeUrl } from '@server/helpers/core-utils'
|
import { sanitizeUrl } from '@server/helpers/core-utils'
|
||||||
import { pickSearchVideoQuery } from '@server/helpers/query'
|
import { pickSearchVideoQuery } from '@server/helpers/query'
|
||||||
import { doJSONRequest, findLatestRedirection } from '@server/helpers/requests'
|
import { doJSONRequest } from '@server/helpers/requests'
|
||||||
import { CONFIG } from '@server/initializers/config'
|
import { CONFIG } from '@server/initializers/config'
|
||||||
import { WEBSERVER } from '@server/initializers/constants'
|
import { WEBSERVER } from '@server/initializers/constants'
|
||||||
|
import { findLatestAPRedirection } from '@server/lib/activitypub/activity'
|
||||||
import { getOrCreateAPVideo } from '@server/lib/activitypub/videos'
|
import { getOrCreateAPVideo } from '@server/lib/activitypub/videos'
|
||||||
import { Hooks } from '@server/lib/plugins/hooks'
|
import { Hooks } from '@server/lib/plugins/hooks'
|
||||||
import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
|
import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
|
||||||
|
@ -141,7 +142,7 @@ async function searchVideoURI (url: string, res: express.Response) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await getOrCreateAPVideo({
|
const result = await getOrCreateAPVideo({
|
||||||
videoObject: await findLatestRedirection(url, { activityPub: true }),
|
videoObject: await findLatestAPRedirection(url),
|
||||||
syncParam
|
syncParam
|
||||||
})
|
})
|
||||||
video = result ? result.video : undefined
|
video = result ? result.video : undefined
|
||||||
|
|
|
@ -21,6 +21,7 @@ type PeerTubeRequestOptions = {
|
||||||
timeout?: number
|
timeout?: number
|
||||||
activityPub?: boolean
|
activityPub?: boolean
|
||||||
bodyKBLimit?: number // 1MB
|
bodyKBLimit?: number // 1MB
|
||||||
|
|
||||||
httpSignature?: {
|
httpSignature?: {
|
||||||
algorithm: string
|
algorithm: string
|
||||||
authorizationHeaderName: string
|
authorizationHeaderName: string
|
||||||
|
@ -28,7 +29,10 @@ type PeerTubeRequestOptions = {
|
||||||
key: string
|
key: string
|
||||||
headers: string[]
|
headers: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonResponse?: boolean
|
jsonResponse?: boolean
|
||||||
|
|
||||||
|
followRedirect?: boolean
|
||||||
} & Pick<GotOptions, 'headers' | 'json' | 'method' | 'searchParams'>
|
} & Pick<GotOptions, 'headers' | 'json' | 'method' | 'searchParams'>
|
||||||
|
|
||||||
const peertubeGot = got.extend({
|
const peertubeGot = got.extend({
|
||||||
|
@ -180,16 +184,6 @@ function isBinaryResponse (result: Response<any>) {
|
||||||
return BINARY_CONTENT_TYPES.has(result.headers['content-type'])
|
return BINARY_CONTENT_TYPES.has(result.headers['content-type'])
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findLatestRedirection (url: string, options: PeerTubeRequestOptions, iteration = 1) {
|
|
||||||
if (iteration > 10) throw new Error('Too much iterations to find final URL ' + url)
|
|
||||||
|
|
||||||
const { headers } = await peertubeGot(url, { followRedirect: false, ...buildGotOptions(options) })
|
|
||||||
|
|
||||||
if (headers.location) return findLatestRedirection(headers.location, options, iteration + 1)
|
|
||||||
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -200,7 +194,6 @@ export {
|
||||||
doRequestAndSaveToFile,
|
doRequestAndSaveToFile,
|
||||||
isBinaryResponse,
|
isBinaryResponse,
|
||||||
getAgent,
|
getAgent,
|
||||||
findLatestRedirection,
|
|
||||||
peertubeGot
|
peertubeGot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +220,7 @@ function buildGotOptions (options: PeerTubeRequestOptions) {
|
||||||
timeout: options.timeout ?? REQUEST_TIMEOUTS.DEFAULT,
|
timeout: options.timeout ?? REQUEST_TIMEOUTS.DEFAULT,
|
||||||
json: options.json,
|
json: options.json,
|
||||||
searchParams: options.searchParams,
|
searchParams: options.searchParams,
|
||||||
|
followRedirect: options.followRedirect,
|
||||||
retry: 2,
|
retry: 2,
|
||||||
headers,
|
headers,
|
||||||
context
|
context
|
||||||
|
|
|
@ -309,7 +309,8 @@ const CONFIG = {
|
||||||
VIDEOS: {
|
VIDEOS: {
|
||||||
FEDERATE_UNLISTED: config.get<boolean>('federation.videos.federate_unlisted'),
|
FEDERATE_UNLISTED: config.get<boolean>('federation.videos.federate_unlisted'),
|
||||||
CLEANUP_REMOTE_INTERACTIONS: config.get<boolean>('federation.videos.cleanup_remote_interactions')
|
CLEANUP_REMOTE_INTERACTIONS: config.get<boolean>('federation.videos.cleanup_remote_interactions')
|
||||||
}
|
},
|
||||||
|
SIGN_FEDERATED_FETCHES: config.get<boolean>('federation.sign_federated_fetches')
|
||||||
},
|
},
|
||||||
PEERTUBE: {
|
PEERTUBE: {
|
||||||
CHECK_LATEST_VERSION: {
|
CHECK_LATEST_VERSION: {
|
||||||
|
|
|
@ -712,7 +712,8 @@ const ACTIVITY_PUB_ACTOR_TYPES: { [ id: string ]: ActivityPubActorType } = {
|
||||||
const HTTP_SIGNATURE = {
|
const HTTP_SIGNATURE = {
|
||||||
HEADER_NAME: 'signature',
|
HEADER_NAME: 'signature',
|
||||||
ALGORITHM: 'rsa-sha256',
|
ALGORITHM: 'rsa-sha256',
|
||||||
HEADERS_TO_SIGN: [ '(request-target)', 'host', 'date', 'digest' ],
|
HEADERS_TO_SIGN_WITH_PAYLOAD: [ '(request-target)', 'host', 'date', 'digest' ],
|
||||||
|
HEADERS_TO_SIGN_WITHOUT_PAYLOAD: [ '(request-target)', 'host', 'date' ],
|
||||||
CLOCK_SKEW_SECONDS: 1800
|
CLOCK_SKEW_SECONDS: 1800
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
import { doJSONRequest } from '@server/helpers/requests'
|
import { doJSONRequest, PeerTubeRequestOptions } from '@server/helpers/requests'
|
||||||
import { APObjectId, ActivityObject, ActivityPubActor, ActivityType } from '@shared/models'
|
import { CONFIG } from '@server/initializers/config'
|
||||||
|
import { ActivityObject, ActivityPubActor, ActivityType, APObjectId } from '@shared/models'
|
||||||
|
import { buildSignedRequestOptions } from './send'
|
||||||
|
|
||||||
function getAPId (object: string | { id: string }) {
|
export function getAPId (object: string | { id: string }) {
|
||||||
if (typeof object === 'string') return object
|
if (typeof object === 'string') return object
|
||||||
|
|
||||||
return object.id
|
return object.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActivityStreamDuration (duration: number) {
|
export function getActivityStreamDuration (duration: number) {
|
||||||
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
|
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
|
||||||
return 'PT' + duration + 'S'
|
return 'PT' + duration + 'S'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDurationFromActivityStream (duration: string) {
|
export function getDurationFromActivityStream (duration: string) {
|
||||||
return parseInt(duration.replace(/[^\d]+/, ''))
|
return parseInt(duration.replace(/[^\d]+/, ''))
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildAvailableActivities (): ActivityType[] {
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function buildAvailableActivities (): ActivityType[] {
|
||||||
return [
|
return [
|
||||||
'Create',
|
'Create',
|
||||||
'Update',
|
'Update',
|
||||||
|
@ -33,9 +37,25 @@ function buildAvailableActivities (): ActivityType[] {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchAPObject <T extends (ActivityObject | ActivityPubActor)> (object: APObjectId) {
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export async function fetchAP <T> (url: string, moreOptions: PeerTubeRequestOptions = {}) {
|
||||||
|
const options = {
|
||||||
|
activityPub: true,
|
||||||
|
|
||||||
|
httpSignature: CONFIG.FEDERATION.SIGN_FEDERATED_FETCHES
|
||||||
|
? await buildSignedRequestOptions({ hasPayload: false })
|
||||||
|
: undefined,
|
||||||
|
|
||||||
|
...moreOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
return doJSONRequest<T>(url, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAPObjectIfNeeded <T extends (ActivityObject | ActivityPubActor)> (object: APObjectId) {
|
||||||
if (typeof object === 'string') {
|
if (typeof object === 'string') {
|
||||||
const { body } = await doJSONRequest<Exclude<T, string>>(object, { activityPub: true })
|
const { body } = await fetchAP<Exclude<T, string>>(object)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
@ -43,10 +63,12 @@ async function fetchAPObject <T extends (ActivityObject | ActivityPubActor)> (ob
|
||||||
return object as Exclude<T, string>
|
return object as Exclude<T, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export async function findLatestAPRedirection (url: string, iteration = 1) {
|
||||||
getAPId,
|
if (iteration > 10) throw new Error('Too much iterations to find final URL ' + url)
|
||||||
fetchAPObject,
|
|
||||||
getActivityStreamDuration,
|
const { headers } = await fetchAP(url, { followRedirect: false })
|
||||||
buildAvailableActivities,
|
|
||||||
getDurationFromActivityStream
|
if (headers.location) return findLatestAPRedirection(headers.location, iteration + 1)
|
||||||
|
|
||||||
|
return url
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ 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 { arrayify } from '@shared/core-utils'
|
import { arrayify } from '@shared/core-utils'
|
||||||
import { ActivityPubActor, APObjectId } from '@shared/models'
|
import { ActivityPubActor, APObjectId } from '@shared/models'
|
||||||
import { fetchAPObject, getAPId } from '../activity'
|
import { fetchAPObjectIfNeeded, 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'
|
||||||
|
@ -87,7 +87,7 @@ async function getOrCreateAPOwner (actorObject: ActivityPubActor, actorUrl: stri
|
||||||
|
|
||||||
async function findOwner (rootUrl: string, attributedTo: APObjectId[] | APObjectId, type: 'Person' | 'Group') {
|
async function findOwner (rootUrl: string, attributedTo: APObjectId[] | APObjectId, type: 'Person' | 'Group') {
|
||||||
for (const actorToCheck of arrayify(attributedTo)) {
|
for (const actorToCheck of arrayify(attributedTo)) {
|
||||||
const actorObject = await fetchAPObject<ActivityPubActor>(getAPId(actorToCheck))
|
const actorObject = await fetchAPObjectIfNeeded<ActivityPubActor>(getAPId(actorToCheck))
|
||||||
|
|
||||||
if (!actorObject) {
|
if (!actorObject) {
|
||||||
logger.warn('Unknown attributed to actor %s for owner %s', actorToCheck, rootUrl)
|
logger.warn('Unknown attributed to actor %s for owner %s', actorToCheck, rootUrl)
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { sanitizeAndCheckActorObject } from '@server/helpers/custom-validators/activitypub/actor'
|
import { sanitizeAndCheckActorObject } from '@server/helpers/custom-validators/activitypub/actor'
|
||||||
import { logger } from '@server/helpers/logger'
|
import { logger } from '@server/helpers/logger'
|
||||||
import { doJSONRequest } from '@server/helpers/requests'
|
|
||||||
import { ActivityPubActor, ActivityPubOrderedCollection } from '@shared/models'
|
import { ActivityPubActor, ActivityPubOrderedCollection } from '@shared/models'
|
||||||
|
import { fetchAP } from '../../activity'
|
||||||
import { checkUrlsSameHost } from '../../url'
|
import { checkUrlsSameHost } from '../../url'
|
||||||
|
|
||||||
async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode: number, actorObject: ActivityPubActor }> {
|
async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode: number, actorObject: ActivityPubActor }> {
|
||||||
logger.info('Fetching remote actor %s.', actorUrl)
|
logger.info('Fetching remote actor %s.', actorUrl)
|
||||||
|
|
||||||
const { body, statusCode } = await doJSONRequest<ActivityPubActor>(actorUrl, { activityPub: true })
|
const { body, statusCode } = await fetchAP<ActivityPubActor>(actorUrl)
|
||||||
|
|
||||||
if (sanitizeAndCheckActorObject(body) === false) {
|
if (sanitizeAndCheckActorObject(body) === false) {
|
||||||
logger.debug('Remote actor JSON is not valid.', { actorJSON: body })
|
logger.debug('Remote actor JSON is not valid.', { actorJSON: body })
|
||||||
|
@ -46,7 +46,7 @@ export {
|
||||||
|
|
||||||
async function fetchActorTotalItems (url: string) {
|
async function fetchActorTotalItems (url: string) {
|
||||||
try {
|
try {
|
||||||
const { body } = await doJSONRequest<ActivityPubOrderedCollection<unknown>>(url, { activityPub: true })
|
const { body } = await fetchAP<ActivityPubOrderedCollection<unknown>>(url)
|
||||||
|
|
||||||
return body.totalItems || 0
|
return body.totalItems || 0
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { URL } from 'url'
|
||||||
import { retryTransactionWrapper } from '@server/helpers/database-utils'
|
import { retryTransactionWrapper } from '@server/helpers/database-utils'
|
||||||
import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub'
|
import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { doJSONRequest } from '../../helpers/requests'
|
|
||||||
import { ACTIVITY_PUB, WEBSERVER } from '../../initializers/constants'
|
import { ACTIVITY_PUB, WEBSERVER } from '../../initializers/constants'
|
||||||
|
import { fetchAP } from './activity'
|
||||||
|
|
||||||
type HandlerFunction<T> = (items: T[]) => (Promise<any> | Bluebird<any>)
|
type HandlerFunction<T> = (items: T[]) => (Promise<any> | Bluebird<any>)
|
||||||
type CleanerFunction = (startedDate: Date) => Promise<any>
|
type CleanerFunction = (startedDate: Date) => Promise<any>
|
||||||
|
@ -14,11 +14,9 @@ async function crawlCollectionPage <T> (argUrl: string, handler: HandlerFunction
|
||||||
|
|
||||||
logger.info('Crawling ActivityPub data on %s.', url)
|
logger.info('Crawling ActivityPub data on %s.', url)
|
||||||
|
|
||||||
const options = { activityPub: true }
|
|
||||||
|
|
||||||
const startDate = new Date()
|
const startDate = new Date()
|
||||||
|
|
||||||
const response = await doJSONRequest<ActivityPubOrderedCollection<T>>(url, options)
|
const response = await fetchAP<ActivityPubOrderedCollection<T>>(url)
|
||||||
const firstBody = response.body
|
const firstBody = response.body
|
||||||
|
|
||||||
const limit = ACTIVITY_PUB.FETCH_PAGE_LIMIT
|
const limit = ACTIVITY_PUB.FETCH_PAGE_LIMIT
|
||||||
|
@ -34,7 +32,7 @@ async function crawlCollectionPage <T> (argUrl: string, handler: HandlerFunction
|
||||||
|
|
||||||
url = nextLink
|
url = nextLink
|
||||||
|
|
||||||
const res = await doJSONRequest<ActivityPubOrderedCollection<T>>(url, options)
|
const res = await fetchAP<ActivityPubOrderedCollection<T>>(url)
|
||||||
body = res.body
|
body = res.body
|
||||||
} else {
|
} else {
|
||||||
// nextLink is already the object we want
|
// nextLink is already the object we want
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '@server/helpers/custom-validators/activitypub/playlist'
|
import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '@server/helpers/custom-validators/activitypub/playlist'
|
||||||
import { isArray } from '@server/helpers/custom-validators/misc'
|
import { isArray } from '@server/helpers/custom-validators/misc'
|
||||||
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
||||||
import { doJSONRequest } from '@server/helpers/requests'
|
|
||||||
import { PlaylistElementObject, PlaylistObject } from '@shared/models'
|
import { PlaylistElementObject, PlaylistObject } from '@shared/models'
|
||||||
|
import { fetchAP } from '../../activity'
|
||||||
import { checkUrlsSameHost } from '../../url'
|
import { checkUrlsSameHost } from '../../url'
|
||||||
|
|
||||||
async function fetchRemoteVideoPlaylist (playlistUrl: string): Promise<{ statusCode: number, playlistObject: PlaylistObject }> {
|
async function fetchRemoteVideoPlaylist (playlistUrl: string): Promise<{ statusCode: number, playlistObject: PlaylistObject }> {
|
||||||
|
@ -10,7 +10,7 @@ async function fetchRemoteVideoPlaylist (playlistUrl: string): Promise<{ statusC
|
||||||
|
|
||||||
logger.info('Fetching remote playlist %s.', playlistUrl, lTags())
|
logger.info('Fetching remote playlist %s.', playlistUrl, lTags())
|
||||||
|
|
||||||
const { body, statusCode } = await doJSONRequest<any>(playlistUrl, { activityPub: true })
|
const { body, statusCode } = await fetchAP<any>(playlistUrl)
|
||||||
|
|
||||||
if (isPlaylistObjectValid(body) === false || checkUrlsSameHost(body.id, playlistUrl) !== true) {
|
if (isPlaylistObjectValid(body) === false || checkUrlsSameHost(body.id, playlistUrl) !== true) {
|
||||||
logger.debug('Remote video playlist JSON is not valid.', { body, ...lTags() })
|
logger.debug('Remote video playlist JSON is not valid.', { body, ...lTags() })
|
||||||
|
@ -30,7 +30,7 @@ async function fetchRemotePlaylistElement (elementUrl: string): Promise<{ status
|
||||||
|
|
||||||
logger.debug('Fetching remote playlist element %s.', elementUrl, lTags())
|
logger.debug('Fetching remote playlist element %s.', elementUrl, lTags())
|
||||||
|
|
||||||
const { body, statusCode } = await doJSONRequest<PlaylistElementObject>(elementUrl, { activityPub: true })
|
const { body, statusCode } = await fetchAP<PlaylistElementObject>(elementUrl)
|
||||||
|
|
||||||
if (!isPlaylistElementObjectValid(body)) throw new Error(`Invalid body in fetch playlist element ${elementUrl}`)
|
if (!isPlaylistElementObjectValid(body)) throw new Error(`Invalid body in fetch playlist element ${elementUrl}`)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { sequelizeTypescript } from '../../../initializers/database'
|
||||||
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
||||||
import { MActorSignature, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../../types/models'
|
import { MActorSignature, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../../types/models'
|
||||||
import { Notifier } from '../../notifier'
|
import { Notifier } from '../../notifier'
|
||||||
import { fetchAPObject } from '../activity'
|
import { fetchAPObjectIfNeeded } from '../activity'
|
||||||
import { createOrUpdateCacheFile } from '../cache-file'
|
import { createOrUpdateCacheFile } from '../cache-file'
|
||||||
import { createOrUpdateLocalVideoViewer } from '../local-video-viewer'
|
import { createOrUpdateLocalVideoViewer } from '../local-video-viewer'
|
||||||
import { createOrUpdateVideoPlaylist } from '../playlists'
|
import { createOrUpdateVideoPlaylist } from '../playlists'
|
||||||
|
@ -31,7 +31,7 @@ async function processCreateActivity (options: APProcessorOptions<ActivityCreate
|
||||||
|
|
||||||
// Only notify if it is not from a fetcher job
|
// Only notify if it is not from a fetcher job
|
||||||
const notify = options.fromFetch !== true
|
const notify = options.fromFetch !== true
|
||||||
const activityObject = await fetchAPObject<Exclude<ActivityObject, AbuseObject>>(activity.object)
|
const activityObject = await fetchAPObjectIfNeeded<Exclude<ActivityObject, AbuseObject>>(activity.object)
|
||||||
const activityType = activityObject.type
|
const activityType = activityObject.type
|
||||||
|
|
||||||
if (activityType === 'Video') {
|
if (activityType === 'Video') {
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { VideoRedundancyModel } from '../../../models/redundancy/video-redundanc
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
||||||
import { MActorSignature } from '../../../types/models'
|
import { MActorSignature } from '../../../types/models'
|
||||||
import { fetchAPObject } from '../activity'
|
import { fetchAPObjectIfNeeded } from '../activity'
|
||||||
import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
|
import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
|
||||||
import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos'
|
import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos'
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ async function processUndoActivity (options: APProcessorOptions<ActivityUndo<Act
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activityToUndo.type === 'Create') {
|
if (activityToUndo.type === 'Create') {
|
||||||
const objectToUndo = await fetchAPObject<CacheFileObject>(activityToUndo.object)
|
const objectToUndo = await fetchAPObjectIfNeeded<CacheFileObject>(activityToUndo.object)
|
||||||
|
|
||||||
if (objectToUndo.type === 'CacheFile') {
|
if (objectToUndo.type === 'CacheFile') {
|
||||||
return retryTransactionWrapper(processUndoCacheFile, byActor, activity, objectToUndo)
|
return retryTransactionWrapper(processUndoCacheFile, byActor, activity, objectToUndo)
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { sequelizeTypescript } from '../../../initializers/database'
|
||||||
import { ActorModel } from '../../../models/actor/actor'
|
import { ActorModel } from '../../../models/actor/actor'
|
||||||
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
||||||
import { MActorFull, MActorSignature } from '../../../types/models'
|
import { MActorFull, MActorSignature } from '../../../types/models'
|
||||||
import { fetchAPObject } from '../activity'
|
import { fetchAPObjectIfNeeded } from '../activity'
|
||||||
import { APActorUpdater } from '../actors/updater'
|
import { APActorUpdater } from '../actors/updater'
|
||||||
import { createOrUpdateCacheFile } from '../cache-file'
|
import { createOrUpdateCacheFile } from '../cache-file'
|
||||||
import { createOrUpdateVideoPlaylist } from '../playlists'
|
import { createOrUpdateVideoPlaylist } from '../playlists'
|
||||||
|
@ -20,7 +20,7 @@ import { APVideoUpdater, getOrCreateAPVideo } from '../videos'
|
||||||
async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate<ActivityUpdateObject>>) {
|
async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate<ActivityUpdateObject>>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
|
||||||
const object = await fetchAPObject(activity.object)
|
const object = await fetchAPObjectIfNeeded(activity.object)
|
||||||
const objectType = object.type
|
const objectType = object.type
|
||||||
|
|
||||||
if (objectType === 'Video') {
|
if (objectType === 'Video') {
|
||||||
|
|
|
@ -23,11 +23,14 @@ async function computeBody <T> (
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildSignedRequestOptions (payload: Payload<any>) {
|
async function buildSignedRequestOptions (options: {
|
||||||
|
signatureActorId?: number
|
||||||
|
hasPayload: boolean
|
||||||
|
}) {
|
||||||
let actor: MActor | null
|
let actor: MActor | null
|
||||||
|
|
||||||
if (payload.signatureActorId) {
|
if (options.signatureActorId) {
|
||||||
actor = await ActorModel.load(payload.signatureActorId)
|
actor = await ActorModel.load(options.signatureActorId)
|
||||||
if (!actor) throw new Error('Unknown signature actor id.')
|
if (!actor) throw new Error('Unknown signature actor id.')
|
||||||
} else {
|
} else {
|
||||||
// We need to sign the request, so use the server
|
// We need to sign the request, so use the server
|
||||||
|
@ -40,7 +43,9 @@ async function buildSignedRequestOptions (payload: Payload<any>) {
|
||||||
authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
|
authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
|
||||||
keyId,
|
keyId,
|
||||||
key: actor.privateKey,
|
key: actor.privateKey,
|
||||||
headers: HTTP_SIGNATURE.HEADERS_TO_SIGN
|
headers: options.hasPayload
|
||||||
|
? HTTP_SIGNATURE.HEADERS_TO_SIGN_WITH_PAYLOAD
|
||||||
|
: HTTP_SIGNATURE.HEADERS_TO_SIGN_WITHOUT_PAYLOAD
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,10 @@ import { map } from 'bluebird'
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { getServerActor } from '@server/models/application/application'
|
import { getServerActor } from '@server/models/application/application'
|
||||||
import { logger, loggerTagsFactory } from '../../helpers/logger'
|
import { logger, loggerTagsFactory } from '../../helpers/logger'
|
||||||
import { doJSONRequest } from '../../helpers/requests'
|
|
||||||
import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
||||||
import { VideoShareModel } from '../../models/video/video-share'
|
import { VideoShareModel } from '../../models/video/video-share'
|
||||||
import { MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../types/models/video'
|
import { MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../types/models/video'
|
||||||
import { getAPId } from './activity'
|
import { fetchAP, getAPId } from './activity'
|
||||||
import { getOrCreateAPActor } from './actors'
|
import { getOrCreateAPActor } from './actors'
|
||||||
import { sendUndoAnnounce, sendVideoAnnounce } from './send'
|
import { sendUndoAnnounce, sendVideoAnnounce } from './send'
|
||||||
import { checkUrlsSameHost, getLocalVideoAnnounceActivityPubUrl } from './url'
|
import { checkUrlsSameHost, getLocalVideoAnnounceActivityPubUrl } from './url'
|
||||||
|
@ -56,7 +55,7 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function addVideoShare (shareUrl: string, video: MVideoId) {
|
async function addVideoShare (shareUrl: string, video: MVideoId) {
|
||||||
const { body } = await doJSONRequest<any>(shareUrl, { activityPub: true })
|
const { body } = await fetchAP<any>(shareUrl)
|
||||||
if (!body?.actor) throw new Error('Body or body actor is invalid')
|
if (!body?.actor) throw new Error('Body or body actor is invalid')
|
||||||
|
|
||||||
const actorUrl = getAPId(body.actor)
|
const actorUrl = getAPId(body.actor)
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { map } from 'bluebird'
|
import { map } from 'bluebird'
|
||||||
|
|
||||||
import { sanitizeAndCheckVideoCommentObject } from '../../helpers/custom-validators/activitypub/video-comments'
|
import { sanitizeAndCheckVideoCommentObject } from '../../helpers/custom-validators/activitypub/video-comments'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { doJSONRequest } from '../../helpers/requests'
|
|
||||||
import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
||||||
import { VideoCommentModel } from '../../models/video/video-comment'
|
import { VideoCommentModel } from '../../models/video/video-comment'
|
||||||
import { MComment, MCommentOwner, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../types/models/video'
|
import { MComment, MCommentOwner, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../types/models/video'
|
||||||
import { isRemoteVideoCommentAccepted } from '../moderation'
|
import { isRemoteVideoCommentAccepted } from '../moderation'
|
||||||
import { Hooks } from '../plugins/hooks'
|
import { Hooks } from '../plugins/hooks'
|
||||||
|
import { fetchAP } from './activity'
|
||||||
import { getOrCreateAPActor } from './actors'
|
import { getOrCreateAPActor } from './actors'
|
||||||
import { checkUrlsSameHost } from './url'
|
import { checkUrlsSameHost } from './url'
|
||||||
import { getOrCreateAPVideo } from './videos'
|
import { getOrCreateAPVideo } from './videos'
|
||||||
|
@ -139,7 +140,7 @@ async function resolveRemoteParentComment (params: ResolveThreadParams) {
|
||||||
throw new Error('Recursion limit reached when resolving a thread')
|
throw new Error('Recursion limit reached when resolving a thread')
|
||||||
}
|
}
|
||||||
|
|
||||||
const { body } = await doJSONRequest<any>(url, { activityPub: true })
|
const { body } = await fetchAP<any>(url)
|
||||||
|
|
||||||
if (sanitizeAndCheckVideoCommentObject(body) === false) {
|
if (sanitizeAndCheckVideoCommentObject(body) === false) {
|
||||||
throw new Error(`Remote video comment JSON ${url} is not valid:` + JSON.stringify(body))
|
throw new Error(`Remote video comment JSON ${url} is not valid:` + JSON.stringify(body))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { sanitizeAndCheckVideoTorrentObject } from '@server/helpers/custom-validators/activitypub/videos'
|
import { sanitizeAndCheckVideoTorrentObject } from '@server/helpers/custom-validators/activitypub/videos'
|
||||||
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
||||||
import { doJSONRequest } from '@server/helpers/requests'
|
|
||||||
import { VideoObject } from '@shared/models'
|
import { VideoObject } from '@shared/models'
|
||||||
|
import { fetchAP } from '../../activity'
|
||||||
import { checkUrlsSameHost } from '../../url'
|
import { checkUrlsSameHost } from '../../url'
|
||||||
|
|
||||||
const lTags = loggerTagsFactory('ap', 'video')
|
const lTags = loggerTagsFactory('ap', 'video')
|
||||||
|
@ -9,7 +9,7 @@ const lTags = loggerTagsFactory('ap', 'video')
|
||||||
async function fetchRemoteVideo (videoUrl: string): Promise<{ statusCode: number, videoObject: VideoObject }> {
|
async function fetchRemoteVideo (videoUrl: string): Promise<{ statusCode: number, videoObject: VideoObject }> {
|
||||||
logger.info('Fetching remote video %s.', videoUrl, lTags(videoUrl))
|
logger.info('Fetching remote video %s.', videoUrl, lTags(videoUrl))
|
||||||
|
|
||||||
const { statusCode, body } = await doJSONRequest<any>(videoUrl, { activityPub: true })
|
const { statusCode, body } = await fetchAP<any>(videoUrl)
|
||||||
|
|
||||||
if (sanitizeAndCheckVideoTorrentObject(body) === false || checkUrlsSameHost(body.id, videoUrl) !== true) {
|
if (sanitizeAndCheckVideoTorrentObject(body) === false || checkUrlsSameHost(body.id, videoUrl) !== true) {
|
||||||
logger.debug('Remote video JSON is not valid.', { body, ...lTags(videoUrl) })
|
logger.debug('Remote video JSON is not valid.', { body, ...lTags(videoUrl) })
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { runInReadCommittedTransaction } from '@server/helpers/database-utils'
|
import { runInReadCommittedTransaction } from '@server/helpers/database-utils'
|
||||||
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
||||||
import { doJSONRequest } from '@server/helpers/requests'
|
|
||||||
import { JobQueue } from '@server/lib/job-queue'
|
import { JobQueue } from '@server/lib/job-queue'
|
||||||
import { VideoModel } from '@server/models/video/video'
|
import { VideoModel } from '@server/models/video/video'
|
||||||
import { VideoCommentModel } from '@server/models/video/video-comment'
|
import { VideoCommentModel } from '@server/models/video/video-comment'
|
||||||
import { VideoShareModel } from '@server/models/video/video-share'
|
import { VideoShareModel } from '@server/models/video/video-share'
|
||||||
import { MVideo } from '@server/types/models'
|
import { MVideo } from '@server/types/models'
|
||||||
import { ActivitypubHttpFetcherPayload, ActivityPubOrderedCollection, VideoObject } from '@shared/models'
|
import { ActivitypubHttpFetcherPayload, ActivityPubOrderedCollection, VideoObject } from '@shared/models'
|
||||||
|
import { fetchAP } from '../../activity'
|
||||||
import { crawlCollectionPage } from '../../crawl'
|
import { crawlCollectionPage } from '../../crawl'
|
||||||
import { addVideoShares } from '../../share'
|
import { addVideoShares } from '../../share'
|
||||||
import { addVideoComments } from '../../video-comments'
|
import { addVideoComments } from '../../video-comments'
|
||||||
|
@ -63,17 +63,15 @@ async function getRatesCount (type: 'like' | 'dislike', video: MVideo, fetchedVi
|
||||||
: fetchedVideo.dislikes
|
: fetchedVideo.dislikes
|
||||||
|
|
||||||
logger.info('Sync %s of video %s', type, video.url)
|
logger.info('Sync %s of video %s', type, video.url)
|
||||||
const options = { activityPub: true }
|
|
||||||
|
|
||||||
const response = await doJSONRequest<ActivityPubOrderedCollection<any>>(uri, options)
|
const { body } = await fetchAP<ActivityPubOrderedCollection<any>>(uri)
|
||||||
const totalItems = response.body.totalItems
|
|
||||||
|
|
||||||
if (isNaN(totalItems)) {
|
if (isNaN(body.totalItems)) {
|
||||||
logger.error('Cannot sync %s of video %s, totalItems is not a number', type, video.url, { body: response.body })
|
logger.error('Cannot sync %s of video %s, totalItems is not a number', type, video.url, { body })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalItems
|
return body.totalItems
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncShares (video: MVideo, fetchedVideo: VideoObject, isSync: boolean) {
|
function syncShares (video: MVideo, fetchedVideo: VideoObject, isSync: boolean) {
|
||||||
|
|
|
@ -6,8 +6,9 @@ import {
|
||||||
isLikeActivityValid
|
isLikeActivityValid
|
||||||
} from '@server/helpers/custom-validators/activitypub/activity'
|
} from '@server/helpers/custom-validators/activitypub/activity'
|
||||||
import { sanitizeAndCheckVideoCommentObject } from '@server/helpers/custom-validators/activitypub/video-comments'
|
import { sanitizeAndCheckVideoCommentObject } from '@server/helpers/custom-validators/activitypub/video-comments'
|
||||||
import { doJSONRequest, PeerTubeRequestError } from '@server/helpers/requests'
|
import { PeerTubeRequestError } from '@server/helpers/requests'
|
||||||
import { AP_CLEANER } from '@server/initializers/constants'
|
import { AP_CLEANER } from '@server/initializers/constants'
|
||||||
|
import { fetchAP } from '@server/lib/activitypub/activity'
|
||||||
import { checkUrlsSameHost } from '@server/lib/activitypub/url'
|
import { checkUrlsSameHost } from '@server/lib/activitypub/url'
|
||||||
import { Redis } from '@server/lib/redis'
|
import { Redis } from '@server/lib/redis'
|
||||||
import { VideoModel } from '@server/models/video/video'
|
import { VideoModel } from '@server/models/video/video'
|
||||||
|
@ -85,7 +86,7 @@ async function updateObjectIfNeeded <T> (options: {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { body } = await doJSONRequest<any>(url, { activityPub: true })
|
const { body } = await fetchAP<any>(url)
|
||||||
|
|
||||||
// If not same id, check same host and update
|
// If not same id, check same host and update
|
||||||
if (!body?.id || !bodyValidator(body)) throw new Error(`Body or body id of ${url} is invalid`)
|
if (!body?.id || !bodyValidator(body)) throw new Error(`Body or body id of ${url} is invalid`)
|
||||||
|
|
|
@ -38,7 +38,7 @@ export {
|
||||||
|
|
||||||
async function buildRequestOptions (payload: ActivitypubHttpBroadcastPayload) {
|
async function buildRequestOptions (payload: ActivitypubHttpBroadcastPayload) {
|
||||||
const body = await computeBody(payload)
|
const body = await computeBody(payload)
|
||||||
const httpSignatureOptions = await buildSignedRequestOptions(payload)
|
const httpSignatureOptions = await buildSignedRequestOptions({ signatureActorId: payload.signatureActorId, hasPayload: true })
|
||||||
|
|
||||||
return {
|
return {
|
||||||
method: 'POST' as 'POST',
|
method: 'POST' as 'POST',
|
||||||
|
|
|
@ -12,7 +12,7 @@ async function processActivityPubHttpUnicast (job: Job) {
|
||||||
const uri = payload.uri
|
const uri = payload.uri
|
||||||
|
|
||||||
const body = await computeBody(payload)
|
const body = await computeBody(payload)
|
||||||
const httpSignatureOptions = await buildSignedRequestOptions(payload)
|
const httpSignatureOptions = await buildSignedRequestOptions({ signatureActorId: payload.signatureActorId, hasPayload: true })
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
method: 'POST' as 'POST',
|
method: 'POST' as 'POST',
|
||||||
|
|
|
@ -58,7 +58,7 @@ async function makeFollowRequest (to: { url: string }, by: { url: string, privat
|
||||||
authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
|
authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
|
||||||
keyId: by.url,
|
keyId: by.url,
|
||||||
key: by.privateKey,
|
key: by.privateKey,
|
||||||
headers: HTTP_SIGNATURE.HEADERS_TO_SIGN
|
headers: HTTP_SIGNATURE.HEADERS_TO_SIGN_WITH_PAYLOAD
|
||||||
}
|
}
|
||||||
const headers = {
|
const headers = {
|
||||||
'digest': buildDigest(body),
|
'digest': buildDigest(body),
|
||||||
|
@ -82,7 +82,7 @@ describe('Test ActivityPub security', function () {
|
||||||
authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
|
authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
|
||||||
keyId: 'acct:peertube@' + servers[1].host,
|
keyId: 'acct:peertube@' + servers[1].host,
|
||||||
key: keys.privateKey,
|
key: keys.privateKey,
|
||||||
headers: HTTP_SIGNATURE.HEADERS_TO_SIGN
|
headers: HTTP_SIGNATURE.HEADERS_TO_SIGN_WITH_PAYLOAD
|
||||||
})
|
})
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
|
@ -12,6 +12,11 @@ webserver:
|
||||||
__name: "PEERTUBE_WEBSERVER_HTTPS"
|
__name: "PEERTUBE_WEBSERVER_HTTPS"
|
||||||
__format: "json"
|
__format: "json"
|
||||||
|
|
||||||
|
federation:
|
||||||
|
sign_federated_fetches:
|
||||||
|
__name: "PEERTUBE_SIGN_FEDERATED_FETCHES"
|
||||||
|
__format: "json"
|
||||||
|
|
||||||
secrets:
|
secrets:
|
||||||
peertube: "PEERTUBE_SECRET"
|
peertube: "PEERTUBE_SECRET"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue