aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2023-03-10 12:01:21 +0100
committerChocobozzz <me@florianbigard.com>2023-03-10 15:45:52 +0100
commit3b504f6ed4e890bebb46d0481aba15b43050323a (patch)
treebbe4c1bc529fec28e0b46263ec92f7c2260ba65a
parent4899138ec5995075c3681ccbd9f6b163fb915991 (diff)
downloadPeerTube-3b504f6ed4e890bebb46d0481aba15b43050323a.tar.gz
PeerTube-3b504f6ed4e890bebb46d0481aba15b43050323a.tar.zst
PeerTube-3b504f6ed4e890bebb46d0481aba15b43050323a.zip
Add ability for plugins to alter video jsonld
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.ts15
-rw-r--r--client/src/app/core/plugins/hooks.service.ts17
-rw-r--r--server/controllers/activitypub/client.ts22
-rw-r--r--server/controllers/activitypub/outbox.ts2
-rw-r--r--server/controllers/activitypub/utils.ts4
-rw-r--r--server/lib/activitypub/context.ts16
-rw-r--r--server/lib/activitypub/send/http.ts4
-rw-r--r--server/lib/activitypub/send/send-create.ts2
-rw-r--r--server/lib/activitypub/send/send-update.ts2
-rw-r--r--server/models/account/account.ts4
-rw-r--r--server/models/video/video-channel.ts4
-rw-r--r--server/models/video/video.ts9
-rw-r--r--server/tests/api/activitypub/security.ts44
-rw-r--r--server/tests/shared/requests.ts29
-rw-r--r--shared/core-utils/i18n/i18n.ts3
-rw-r--r--shared/models/plugins/server/server-hook.model.ts8
16 files changed, 91 insertions, 94 deletions
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.ts b/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.ts
index ebfb42711..1834f7be5 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.ts
@@ -22,11 +22,11 @@ export class VideoAttributesComponent implements OnInit {
22 constructor (private hooks: HooksService) { } 22 constructor (private hooks: HooksService) { }
23 23
24 async ngOnInit () { 24 async ngOnInit () {
25 this.pluginMetadata = await this.hooks.wrapFunResult( 25 this.pluginMetadata = await this.hooks.wrapObject(
26 this.buildPluginMetadata.bind(this), 26 this.pluginMetadata,
27 { video: this.video },
28 'video-watch', 27 'video-watch',
29 'filter:video-watch.video-plugin-metadata.result' 28 'filter:video-watch.video-plugin-metadata.result',
29 { video: this.video }
30 ) 30 )
31 } 31 }
32 32
@@ -39,11 +39,4 @@ export class VideoAttributesComponent implements OnInit {
39 39
40 return this.video.tags 40 return this.video.tags
41 } 41 }
42
43 // Used for plugin hooks
44 private buildPluginMetadata (_options: {
45 video: VideoDetails
46 }): PluginMetadata[] {
47 return []
48 }
49} 42}
diff --git a/client/src/app/core/plugins/hooks.service.ts b/client/src/app/core/plugins/hooks.service.ts
index f325605e9..d9fef8389 100644
--- a/client/src/app/core/plugins/hooks.service.ts
+++ b/client/src/app/core/plugins/hooks.service.ts
@@ -48,15 +48,6 @@ export class HooksService {
48 return this.pluginService.runHook(hookResultName, result, params) 48 return this.pluginService.runHook(hookResultName, result, params)
49 } 49 }
50 50
51 async wrapFunResult <P, R, H extends ClientFilterHookName>
52 (fun: RawFunction<P, R>, params: P, scope: PluginClientScope, hookResultName: H) {
53 await this.pluginService.ensurePluginsAreLoaded(scope)
54
55 const result = fun(params)
56
57 return this.pluginService.runHook(hookResultName, result, params)
58 }
59
60 runAction<T, U extends ClientActionHookName> (hookName: U, scope: PluginClientScope, params?: T) { 51 runAction<T, U extends ClientActionHookName> (hookName: U, scope: PluginClientScope, params?: T) {
61 // Use setTimeout to give priority to Angular change detector 52 // Use setTimeout to give priority to Angular change detector
62 setTimeout(() => { 53 setTimeout(() => {
@@ -66,13 +57,13 @@ export class HooksService {
66 }) 57 })
67 } 58 }
68 59
69 async wrapObject<T, U extends ClientFilterHookName> (result: T, scope: PluginClientScope, hookName: U) { 60 async wrapObject<T, U extends ClientFilterHookName> (result: T, scope: PluginClientScope, hookName: U, context?: any) {
70 await this.pluginService.ensurePluginsAreLoaded(scope) 61 await this.pluginService.ensurePluginsAreLoaded(scope)
71 62
72 return this.wrapObjectWithoutScopeLoad(result, hookName) 63 return this.wrapObjectWithoutScopeLoad(result, hookName, context)
73 } 64 }
74 65
75 private wrapObjectWithoutScopeLoad<T, U extends ClientFilterHookName> (result: T, hookName: U) { 66 private wrapObjectWithoutScopeLoad<T, U extends ClientFilterHookName> (result: T, hookName: U, context?: any) {
76 return this.pluginService.runHook(hookName, result) 67 return this.pluginService.runHook(hookName, result, context)
77 } 68 }
78} 69}
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts
index def320730..750e3091c 100644
--- a/server/controllers/activitypub/client.ts
+++ b/server/controllers/activitypub/client.ts
@@ -48,7 +48,7 @@ activityPubClientRouter.get(
48 [ '/accounts?/:name', '/accounts?/:name/video-channels', '/a/:name', '/a/:name/video-channels' ], 48 [ '/accounts?/:name', '/accounts?/:name/video-channels', '/a/:name', '/a/:name/video-channels' ],
49 executeIfActivityPub, 49 executeIfActivityPub,
50 asyncMiddleware(localAccountValidator), 50 asyncMiddleware(localAccountValidator),
51 accountController 51 asyncMiddleware(accountController)
52) 52)
53activityPubClientRouter.get('/accounts?/:name/followers', 53activityPubClientRouter.get('/accounts?/:name/followers',
54 executeIfActivityPub, 54 executeIfActivityPub,
@@ -69,13 +69,13 @@ activityPubClientRouter.get('/accounts?/:name/likes/:videoId',
69 executeIfActivityPub, 69 executeIfActivityPub,
70 cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS), 70 cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
71 asyncMiddleware(getAccountVideoRateValidatorFactory('like')), 71 asyncMiddleware(getAccountVideoRateValidatorFactory('like')),
72 getAccountVideoRateFactory('like') 72 asyncMiddleware(getAccountVideoRateFactory('like'))
73) 73)
74activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', 74activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId',
75 executeIfActivityPub, 75 executeIfActivityPub,
76 cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS), 76 cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
77 asyncMiddleware(getAccountVideoRateValidatorFactory('dislike')), 77 asyncMiddleware(getAccountVideoRateValidatorFactory('dislike')),
78 getAccountVideoRateFactory('dislike') 78 asyncMiddleware(getAccountVideoRateFactory('dislike'))
79) 79)
80 80
81activityPubClientRouter.get( 81activityPubClientRouter.get(
@@ -131,7 +131,7 @@ activityPubClientRouter.get(
131 executeIfActivityPub, 131 executeIfActivityPub,
132 asyncMiddleware(videoChannelsNameWithHostValidator), 132 asyncMiddleware(videoChannelsNameWithHostValidator),
133 ensureIsLocalChannel, 133 ensureIsLocalChannel,
134 videoChannelController 134 asyncMiddleware(videoChannelController)
135) 135)
136activityPubClientRouter.get('/video-channels/:nameWithHost/followers', 136activityPubClientRouter.get('/video-channels/:nameWithHost/followers',
137 executeIfActivityPub, 137 executeIfActivityPub,
@@ -172,13 +172,13 @@ activityPubClientRouter.get(
172activityPubClientRouter.get('/video-playlists/:playlistId/videos/:playlistElementId', 172activityPubClientRouter.get('/video-playlists/:playlistId/videos/:playlistElementId',
173 executeIfActivityPub, 173 executeIfActivityPub,
174 asyncMiddleware(videoPlaylistElementAPGetValidator), 174 asyncMiddleware(videoPlaylistElementAPGetValidator),
175 videoPlaylistElementController 175 asyncMiddleware(videoPlaylistElementController)
176) 176)
177 177
178activityPubClientRouter.get('/videos/local-viewer/:localViewerId', 178activityPubClientRouter.get('/videos/local-viewer/:localViewerId',
179 executeIfActivityPub, 179 executeIfActivityPub,
180 asyncMiddleware(getVideoLocalViewerValidator), 180 asyncMiddleware(getVideoLocalViewerValidator),
181 getVideoLocalViewerController 181 asyncMiddleware(getVideoLocalViewerController)
182) 182)
183 183
184// --------------------------------------------------------------------------- 184// ---------------------------------------------------------------------------
@@ -189,10 +189,10 @@ export {
189 189
190// --------------------------------------------------------------------------- 190// ---------------------------------------------------------------------------
191 191
192function accountController (req: express.Request, res: express.Response) { 192async function accountController (req: express.Request, res: express.Response) {
193 const account = res.locals.account 193 const account = res.locals.account
194 194
195 return activityPubResponse(activityPubContextify(account.toActivityPubObject(), 'Actor'), res) 195 return activityPubResponse(activityPubContextify(await account.toActivityPubObject(), 'Actor'), res)
196} 196}
197 197
198async function accountFollowersController (req: express.Request, res: express.Response) { 198async function accountFollowersController (req: express.Request, res: express.Response) {
@@ -246,7 +246,7 @@ async function videoController (req: express.Request, res: express.Response) {
246 const videoWithCaptions = Object.assign(video, { VideoCaptions: captions }) 246 const videoWithCaptions = Object.assign(video, { VideoCaptions: captions })
247 247
248 const audience = getAudience(videoWithCaptions.VideoChannel.Account.Actor, videoWithCaptions.privacy === VideoPrivacy.PUBLIC) 248 const audience = getAudience(videoWithCaptions.VideoChannel.Account.Actor, videoWithCaptions.privacy === VideoPrivacy.PUBLIC)
249 const videoObject = audiencify(videoWithCaptions.toActivityPubObject(), audience) 249 const videoObject = audiencify(await videoWithCaptions.toActivityPubObject(), audience)
250 250
251 if (req.path.endsWith('/activity')) { 251 if (req.path.endsWith('/activity')) {
252 const data = buildCreateActivity(videoWithCaptions.url, video.VideoChannel.Account.Actor, videoObject, audience) 252 const data = buildCreateActivity(videoWithCaptions.url, video.VideoChannel.Account.Actor, videoObject, audience)
@@ -321,10 +321,10 @@ async function videoCommentsController (req: express.Request, res: express.Respo
321 return activityPubResponse(activityPubContextify(json, 'Collection'), res) 321 return activityPubResponse(activityPubContextify(json, 'Collection'), res)
322} 322}
323 323
324function videoChannelController (req: express.Request, res: express.Response) { 324async function videoChannelController (req: express.Request, res: express.Response) {
325 const videoChannel = res.locals.videoChannel 325 const videoChannel = res.locals.videoChannel
326 326
327 return activityPubResponse(activityPubContextify(videoChannel.toActivityPubObject(), 'Actor'), res) 327 return activityPubResponse(activityPubContextify(await videoChannel.toActivityPubObject(), 'Actor'), res)
328} 328}
329 329
330async function videoChannelFollowersController (req: express.Request, res: express.Response) { 330async function videoChannelFollowersController (req: express.Request, res: express.Response) {
diff --git a/server/controllers/activitypub/outbox.ts b/server/controllers/activitypub/outbox.ts
index f385c9927..681a5660c 100644
--- a/server/controllers/activitypub/outbox.ts
+++ b/server/controllers/activitypub/outbox.ts
@@ -63,7 +63,7 @@ async function buildActivities (actor: MActorLight, start: number, count: number
63 63
64 activities.push(announceActivity) 64 activities.push(announceActivity)
65 } else { 65 } else {
66 const videoObject = video.toActivityPubObject() 66 const videoObject = await video.toActivityPubObject()
67 const createActivity = buildCreateActivity(video.url, byActor, videoObject, createActivityAudience) 67 const createActivity = buildCreateActivity(video.url, byActor, videoObject, createActivityAudience)
68 68
69 activities.push(createActivity) 69 activities.push(createActivity)
diff --git a/server/controllers/activitypub/utils.ts b/server/controllers/activitypub/utils.ts
index f851ef652..5de38eb43 100644
--- a/server/controllers/activitypub/utils.ts
+++ b/server/controllers/activitypub/utils.ts
@@ -1,6 +1,8 @@
1import express from 'express' 1import express from 'express'
2 2
3function activityPubResponse (data: any, res: express.Response) { 3async function activityPubResponse (promise: Promise<any>, res: express.Response) {
4 const data = await promise
5
4 return res.type('application/activity+json; charset=utf-8') 6 return res.type('application/activity+json; charset=utf-8')
5 .json(data) 7 .json(data)
6} 8}
diff --git a/server/lib/activitypub/context.ts b/server/lib/activitypub/context.ts
index 349c4d227..a3ca52a31 100644
--- a/server/lib/activitypub/context.ts
+++ b/server/lib/activitypub/context.ts
@@ -1,7 +1,8 @@
1import { ContextType } from '@shared/models' 1import { ContextType } from '@shared/models'
2import { Hooks } from '../plugins/hooks'
2 3
3function activityPubContextify <T> (data: T, type: ContextType) { 4async function activityPubContextify <T> (data: T, type: ContextType) {
4 return { ...getContextData(type), ...data } 5 return { ...await getContextData(type), ...data }
5} 6}
6 7
7// --------------------------------------------------------------------------- 8// ---------------------------------------------------------------------------
@@ -165,10 +166,13 @@ const contextStore: { [ id in ContextType ]: (string | { [ id: string ]: string
165 Rate: buildContext() 166 Rate: buildContext()
166} 167}
167 168
168function getContextData (type: ContextType) { 169async function getContextData (type: ContextType) {
169 return { 170 const contextData = await Hooks.wrapObject(
170 '@context': contextStore[type] 171 contextStore[type],
171 } 172 'filter:activity-pub.activity.context.build.result'
173 )
174
175 return { '@context': contextData }
172} 176}
173 177
174function buildContext (contextValue?: ContextValue) { 178function buildContext (contextValue?: ContextValue) {
diff --git a/server/lib/activitypub/send/http.ts b/server/lib/activitypub/send/http.ts
index d8d0b8542..ad7869853 100644
--- a/server/lib/activitypub/send/http.ts
+++ b/server/lib/activitypub/send/http.ts
@@ -52,9 +52,9 @@ function buildGlobalHeaders (body: any) {
52 } 52 }
53} 53}
54 54
55function signAndContextify <T> (byActor: MActor, data: T, contextType: ContextType | null) { 55async function signAndContextify <T> (byActor: MActor, data: T, contextType: ContextType | null) {
56 const activity = contextType 56 const activity = contextType
57 ? activityPubContextify(data, contextType) 57 ? await activityPubContextify(data, contextType)
58 : data 58 : data
59 59
60 return signJsonLDObject(byActor, activity) 60 return signJsonLDObject(byActor, activity)
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts
index 7c3a6bdd0..0e996ab80 100644
--- a/server/lib/activitypub/send/send-create.ts
+++ b/server/lib/activitypub/send/send-create.ts
@@ -33,7 +33,7 @@ async function sendCreateVideo (video: MVideoAP, transaction: Transaction) {
33 logger.info('Creating job to send video creation of %s.', video.url, lTags(video.uuid)) 33 logger.info('Creating job to send video creation of %s.', video.url, lTags(video.uuid))
34 34
35 const byActor = video.VideoChannel.Account.Actor 35 const byActor = video.VideoChannel.Account.Actor
36 const videoObject = video.toActivityPubObject() 36 const videoObject = await video.toActivityPubObject()
37 37
38 const audience = getAudience(byActor, video.privacy === VideoPrivacy.PUBLIC) 38 const audience = getAudience(byActor, video.privacy === VideoPrivacy.PUBLIC)
39 const createActivity = buildCreateActivity(video.url, byActor, videoObject, audience) 39 const createActivity = buildCreateActivity(video.url, byActor, videoObject, audience)
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts
index 24983dd19..5a66294e6 100644
--- a/server/lib/activitypub/send/send-update.ts
+++ b/server/lib/activitypub/send/send-update.ts
@@ -36,7 +36,7 @@ async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, transaction: T
36 video.VideoCaptions = await video.$get('VideoCaptions', { transaction }) 36 video.VideoCaptions = await video.$get('VideoCaptions', { transaction })
37 } 37 }
38 38
39 const videoObject = video.toActivityPubObject() 39 const videoObject = await video.toActivityPubObject()
40 const audience = getAudience(byActor, video.privacy === VideoPrivacy.PUBLIC) 40 const audience = getAudience(byActor, video.privacy === VideoPrivacy.PUBLIC)
41 41
42 const updateActivity = buildUpdateActivity(url, byActor, videoObject, audience) 42 const updateActivity = buildUpdateActivity(url, byActor, videoObject, audience)
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index dc989417b..5bf29f45a 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -447,8 +447,8 @@ export class AccountModel extends Model<Partial<AttributesOnly<AccountModel>>> {
447 } 447 }
448 } 448 }
449 449
450 toActivityPubObject (this: MAccountAP) { 450 async toActivityPubObject (this: MAccountAP) {
451 const obj = this.Actor.toActivityPubObject(this.name) 451 const obj = await this.Actor.toActivityPubObject(this.name)
452 452
453 return Object.assign(obj, { 453 return Object.assign(obj, {
454 summary: this.description 454 summary: this.description
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index b71f5a197..67fccab68 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -809,8 +809,8 @@ export class VideoChannelModel extends Model<Partial<AttributesOnly<VideoChannel
809 return Object.assign(actor, videoChannel) 809 return Object.assign(actor, videoChannel)
810 } 810 }
811 811
812 toActivityPubObject (this: MChannelAP): ActivityPubActor { 812 async toActivityPubObject (this: MChannelAP): Promise<ActivityPubActor> {
813 const obj = this.Actor.toActivityPubObject(this.name) 813 const obj = await this.Actor.toActivityPubObject(this.name)
814 814
815 return Object.assign(obj, { 815 return Object.assign(obj, {
816 summary: this.description, 816 summary: this.description,
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 1a10d2da2..7fe2ec293 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -137,6 +137,7 @@ import { VideoShareModel } from './video-share'
137import { VideoSourceModel } from './video-source' 137import { VideoSourceModel } from './video-source'
138import { VideoStreamingPlaylistModel } from './video-streaming-playlist' 138import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
139import { VideoTagModel } from './video-tag' 139import { VideoTagModel } from './video-tag'
140import { Hooks } from '@server/lib/plugins/hooks'
140 141
141export enum ScopeNames { 142export enum ScopeNames {
142 FOR_API = 'FOR_API', 143 FOR_API = 'FOR_API',
@@ -1713,8 +1714,12 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1713 return files 1714 return files
1714 } 1715 }
1715 1716
1716 toActivityPubObject (this: MVideoAP): VideoObject { 1717 toActivityPubObject (this: MVideoAP): Promise<VideoObject> {
1717 return videoModelToActivityPubObject(this) 1718 return Hooks.wrapObject(
1719 videoModelToActivityPubObject(this),
1720 'filter:activity-pub.video.jsonld.build.result',
1721 { video: this }
1722 )
1718 } 1723 }
1719 1724
1720 getTruncatedDescription () { 1725 getTruncatedDescription () {
diff --git a/server/tests/api/activitypub/security.ts b/server/tests/api/activitypub/security.ts
index 22fae8331..c6f171633 100644
--- a/server/tests/api/activitypub/security.ts
+++ b/server/tests/api/activitypub/security.ts
@@ -2,10 +2,10 @@
2 2
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { buildDigest } from '@server/helpers/peertube-crypto' 4import { buildDigest } from '@server/helpers/peertube-crypto'
5import { HTTP_SIGNATURE } from '@server/initializers/constants' 5import { ACTIVITY_PUB, HTTP_SIGNATURE } from '@server/initializers/constants'
6import { activityPubContextify } from '@server/lib/activitypub/context' 6import { activityPubContextify } from '@server/lib/activitypub/context'
7import { buildGlobalHeaders, signAndContextify } from '@server/lib/activitypub/send' 7import { buildGlobalHeaders, signAndContextify } from '@server/lib/activitypub/send'
8import { makeFollowRequest, makePOSTAPRequest } from '@server/tests/shared' 8import { makePOSTAPRequest } from '@server/tests/shared'
9import { buildAbsoluteFixturePath, wait } from '@shared/core-utils' 9import { buildAbsoluteFixturePath, wait } from '@shared/core-utils'
10import { HttpStatusCode } from '@shared/models' 10import { HttpStatusCode } from '@shared/models'
11import { cleanupTests, createMultipleServers, killallServers, PeerTubeServer } from '@shared/server-commands' 11import { cleanupTests, createMultipleServers, killallServers, PeerTubeServer } from '@shared/server-commands'
@@ -43,6 +43,32 @@ function getAnnounceWithoutContext (server: PeerTubeServer) {
43 return result 43 return result
44} 44}
45 45
46async function makeFollowRequest (to: { url: string }, by: { url: string, privateKey }) {
47 const follow = {
48 type: 'Follow',
49 id: by.url + '/' + new Date().getTime(),
50 actor: by.url,
51 object: to.url
52 }
53
54 const body = await activityPubContextify(follow, 'Follow')
55
56 const httpSignature = {
57 algorithm: HTTP_SIGNATURE.ALGORITHM,
58 authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
59 keyId: by.url,
60 key: by.privateKey,
61 headers: HTTP_SIGNATURE.HEADERS_TO_SIGN
62 }
63 const headers = {
64 'digest': buildDigest(body),
65 'content-type': 'application/activity+json',
66 'accept': ACTIVITY_PUB.ACCEPT_HEADER
67 }
68
69 return makePOSTAPRequest(to.url + '/inbox', body, httpSignature, headers)
70}
71
46describe('Test ActivityPub security', function () { 72describe('Test ActivityPub security', function () {
47 let servers: PeerTubeServer[] 73 let servers: PeerTubeServer[]
48 let url: string 74 let url: string
@@ -77,7 +103,7 @@ describe('Test ActivityPub security', function () {
77 describe('When checking HTTP signature', function () { 103 describe('When checking HTTP signature', function () {
78 104
79 it('Should fail with an invalid digest', async function () { 105 it('Should fail with an invalid digest', async function () {
80 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') 106 const body = await activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
81 const headers = { 107 const headers = {
82 Digest: buildDigest({ hello: 'coucou' }) 108 Digest: buildDigest({ hello: 'coucou' })
83 } 109 }
@@ -91,7 +117,7 @@ describe('Test ActivityPub security', function () {
91 }) 117 })
92 118
93 it('Should fail with an invalid date', async function () { 119 it('Should fail with an invalid date', async function () {
94 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') 120 const body = await activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
95 const headers = buildGlobalHeaders(body) 121 const headers = buildGlobalHeaders(body)
96 headers['date'] = 'Wed, 21 Oct 2015 07:28:00 GMT' 122 headers['date'] = 'Wed, 21 Oct 2015 07:28:00 GMT'
97 123
@@ -107,7 +133,7 @@ describe('Test ActivityPub security', function () {
107 await setKeysOfServer(servers[0], servers[1], invalidKeys.publicKey, invalidKeys.privateKey) 133 await setKeysOfServer(servers[0], servers[1], invalidKeys.publicKey, invalidKeys.privateKey)
108 await setKeysOfServer(servers[1], servers[1], invalidKeys.publicKey, invalidKeys.privateKey) 134 await setKeysOfServer(servers[1], servers[1], invalidKeys.publicKey, invalidKeys.privateKey)
109 135
110 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') 136 const body = await activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
111 const headers = buildGlobalHeaders(body) 137 const headers = buildGlobalHeaders(body)
112 138
113 try { 139 try {
@@ -122,7 +148,7 @@ describe('Test ActivityPub security', function () {
122 await setKeysOfServer(servers[0], servers[1], keys.publicKey, keys.privateKey) 148 await setKeysOfServer(servers[0], servers[1], keys.publicKey, keys.privateKey)
123 await setKeysOfServer(servers[1], servers[1], keys.publicKey, keys.privateKey) 149 await setKeysOfServer(servers[1], servers[1], keys.publicKey, keys.privateKey)
124 150
125 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') 151 const body = await activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
126 const headers = buildGlobalHeaders(body) 152 const headers = buildGlobalHeaders(body)
127 153
128 const signatureOptions = baseHttpSignature() 154 const signatureOptions = baseHttpSignature()
@@ -145,7 +171,7 @@ describe('Test ActivityPub security', function () {
145 }) 171 })
146 172
147 it('Should succeed with a valid HTTP signature draft 11 (without date but with (created))', async function () { 173 it('Should succeed with a valid HTTP signature draft 11 (without date but with (created))', async function () {
148 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') 174 const body = await activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
149 const headers = buildGlobalHeaders(body) 175 const headers = buildGlobalHeaders(body)
150 176
151 const signatureOptions = baseHttpSignature() 177 const signatureOptions = baseHttpSignature()
@@ -156,7 +182,7 @@ describe('Test ActivityPub security', function () {
156 }) 182 })
157 183
158 it('Should succeed with a valid HTTP signature', async function () { 184 it('Should succeed with a valid HTTP signature', async function () {
159 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') 185 const body = await activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
160 const headers = buildGlobalHeaders(body) 186 const headers = buildGlobalHeaders(body)
161 187
162 const { statusCode } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) 188 const { statusCode } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers)
@@ -175,7 +201,7 @@ describe('Test ActivityPub security', function () {
175 await killallServers([ servers[1] ]) 201 await killallServers([ servers[1] ])
176 await servers[1].run() 202 await servers[1].run()
177 203
178 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') 204 const body = await activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
179 const headers = buildGlobalHeaders(body) 205 const headers = buildGlobalHeaders(body)
180 206
181 try { 207 try {
diff --git a/server/tests/shared/requests.ts b/server/tests/shared/requests.ts
index 57120caca..0cfeab7b2 100644
--- a/server/tests/shared/requests.ts
+++ b/server/tests/shared/requests.ts
@@ -1,7 +1,4 @@
1import { buildDigest } from '@server/helpers/peertube-crypto'
2import { doRequest } from '@server/helpers/requests' 1import { doRequest } from '@server/helpers/requests'
3import { ACTIVITY_PUB, HTTP_SIGNATURE } from '@server/initializers/constants'
4import { activityPubContextify } from '@server/lib/activitypub/context'
5 2
6export function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) { 3export function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) {
7 const options = { 4 const options = {
@@ -13,29 +10,3 @@ export function makePOSTAPRequest (url: string, body: any, httpSignature: any, h
13 10
14 return doRequest(url, options) 11 return doRequest(url, options)
15} 12}
16
17export async function makeFollowRequest (to: { url: string }, by: { url: string, privateKey }) {
18 const follow = {
19 type: 'Follow',
20 id: by.url + '/' + new Date().getTime(),
21 actor: by.url,
22 object: to.url
23 }
24
25 const body = activityPubContextify(follow, 'Follow')
26
27 const httpSignature = {
28 algorithm: HTTP_SIGNATURE.ALGORITHM,
29 authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
30 keyId: by.url,
31 key: by.privateKey,
32 headers: HTTP_SIGNATURE.HEADERS_TO_SIGN
33 }
34 const headers = {
35 'digest': buildDigest(body),
36 'content-type': 'application/activity+json',
37 'accept': ACTIVITY_PUB.ACCEPT_HEADER
38 }
39
40 return makePOSTAPRequest(to.url + '/inbox', body, httpSignature, headers)
41}
diff --git a/shared/core-utils/i18n/i18n.ts b/shared/core-utils/i18n/i18n.ts
index c43e249a6..38c1b0cc9 100644
--- a/shared/core-utils/i18n/i18n.ts
+++ b/shared/core-utils/i18n/i18n.ts
@@ -75,8 +75,7 @@ const I18N_LOCALE_ALIAS = {
75 'zh': 'zh-Hans-CN' 75 'zh': 'zh-Hans-CN'
76} 76}
77 77
78export const POSSIBLE_LOCALES = Object.keys(I18N_LOCALES) 78export const POSSIBLE_LOCALES = (Object.keys(I18N_LOCALES) as string[]).concat(Object.keys(I18N_LOCALE_ALIAS))
79 .concat(Object.keys(I18N_LOCALE_ALIAS))
80 79
81export function getDefaultLocale () { 80export function getDefaultLocale () {
82 return 'en-US' 81 return 'en-US'
diff --git a/shared/models/plugins/server/server-hook.model.ts b/shared/models/plugins/server/server-hook.model.ts
index bbd08365c..7881beaa2 100644
--- a/shared/models/plugins/server/server-hook.model.ts
+++ b/shared/models/plugins/server/server-hook.model.ts
@@ -113,7 +113,13 @@ export const serverFilterHookObject = {
113 'filter:transcoding.manual.resolutions-to-transcode.result': true, 113 'filter:transcoding.manual.resolutions-to-transcode.result': true,
114 'filter:transcoding.auto.resolutions-to-transcode.result': true, 114 'filter:transcoding.auto.resolutions-to-transcode.result': true,
115 115
116 'filter:activity-pub.remote-video-comment.create.accept.result': true 116 'filter:activity-pub.remote-video-comment.create.accept.result': true,
117
118 'filter:activity-pub.activity.context.build.result': true,
119
120 // Filter the result of video JSON LD builder
121 // You may also need to use filter:activity-pub.activity.context.build.result to also update JSON LD context
122 'filter:activity-pub.video.jsonld.build.result': true
117} 123}
118 124
119export type ServerFilterHookName = keyof typeof serverFilterHookObject 125export type ServerFilterHookName = keyof typeof serverFilterHookObject