diff options
author | Chocobozzz <me@florianbigard.com> | 2023-03-10 12:01:21 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2023-03-10 14:42:18 +0100 |
commit | 866b5d3f5230204d611a556260102996c1aefe10 (patch) | |
tree | d6d1e900ce81a7f1edc121089c10a67abba855eb | |
parent | 4265d90b0061399e23b816e3880ee1be47ead96f (diff) | |
download | PeerTube-866b5d3f5230204d611a556260102996c1aefe10.tar.gz PeerTube-866b5d3f5230204d611a556260102996c1aefe10.tar.zst PeerTube-866b5d3f5230204d611a556260102996c1aefe10.zip |
Add ability for plugins to alter video jsonld
-rw-r--r-- | client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.ts | 15 | ||||
-rw-r--r-- | client/src/app/core/plugins/hooks.service.ts | 17 | ||||
-rw-r--r-- | server/controllers/activitypub/client.ts | 22 | ||||
-rw-r--r-- | server/controllers/activitypub/outbox.ts | 2 | ||||
-rw-r--r-- | server/controllers/activitypub/utils.ts | 4 | ||||
-rw-r--r-- | server/lib/activitypub/context.ts | 16 | ||||
-rw-r--r-- | server/lib/activitypub/send/http.ts | 4 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-create.ts | 2 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-update.ts | 2 | ||||
-rw-r--r-- | server/models/account/account.ts | 4 | ||||
-rw-r--r-- | server/models/video/video-channel.ts | 4 | ||||
-rw-r--r-- | server/models/video/video.ts | 9 | ||||
-rw-r--r-- | server/tests/api/activitypub/security.ts | 44 | ||||
-rw-r--r-- | server/tests/shared/requests.ts | 29 | ||||
-rw-r--r-- | shared/core-utils/i18n/i18n.ts | 3 | ||||
-rw-r--r-- | shared/models/plugins/server/server-hook.model.ts | 8 |
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 | ) |
53 | activityPubClientRouter.get('/accounts?/:name/followers', | 53 | activityPubClientRouter.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 | ) |
74 | activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', | 74 | activityPubClientRouter.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 | ||
81 | activityPubClientRouter.get( | 81 | activityPubClientRouter.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 | ) |
136 | activityPubClientRouter.get('/video-channels/:nameWithHost/followers', | 136 | activityPubClientRouter.get('/video-channels/:nameWithHost/followers', |
137 | executeIfActivityPub, | 137 | executeIfActivityPub, |
@@ -172,13 +172,13 @@ activityPubClientRouter.get( | |||
172 | activityPubClientRouter.get('/video-playlists/:playlistId/videos/:playlistElementId', | 172 | activityPubClientRouter.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 | ||
178 | activityPubClientRouter.get('/videos/local-viewer/:localViewerId', | 178 | activityPubClientRouter.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 | ||
192 | function accountController (req: express.Request, res: express.Response) { | 192 | async 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 | ||
198 | async function accountFollowersController (req: express.Request, res: express.Response) { | 198 | async 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 | ||
324 | function videoChannelController (req: express.Request, res: express.Response) { | 324 | async 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 | ||
330 | async function videoChannelFollowersController (req: express.Request, res: express.Response) { | 330 | async 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 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | 2 | ||
3 | function activityPubResponse (data: any, res: express.Response) { | 3 | async 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 @@ | |||
1 | import { ContextType } from '@shared/models' | 1 | import { ContextType } from '@shared/models' |
2 | import { Hooks } from '../plugins/hooks' | ||
2 | 3 | ||
3 | function activityPubContextify <T> (data: T, type: ContextType) { | 4 | async 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 | ||
168 | function getContextData (type: ContextType) { | 169 | async 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 | ||
174 | function buildContext (contextValue?: ContextValue) { | 178 | function 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 | ||
55 | function signAndContextify <T> (byActor: MActor, data: T, contextType: ContextType | null) { | 55 | async 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' | |||
137 | import { VideoSourceModel } from './video-source' | 137 | import { VideoSourceModel } from './video-source' |
138 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' | 138 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' |
139 | import { VideoTagModel } from './video-tag' | 139 | import { VideoTagModel } from './video-tag' |
140 | import { Hooks } from '@server/lib/plugins/hooks' | ||
140 | 141 | ||
141 | export enum ScopeNames { | 142 | export 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 | ||
3 | import { expect } from 'chai' | 3 | import { expect } from 'chai' |
4 | import { buildDigest } from '@server/helpers/peertube-crypto' | 4 | import { buildDigest } from '@server/helpers/peertube-crypto' |
5 | import { HTTP_SIGNATURE } from '@server/initializers/constants' | 5 | import { ACTIVITY_PUB, HTTP_SIGNATURE } from '@server/initializers/constants' |
6 | import { activityPubContextify } from '@server/lib/activitypub/context' | 6 | import { activityPubContextify } from '@server/lib/activitypub/context' |
7 | import { buildGlobalHeaders, signAndContextify } from '@server/lib/activitypub/send' | 7 | import { buildGlobalHeaders, signAndContextify } from '@server/lib/activitypub/send' |
8 | import { makeFollowRequest, makePOSTAPRequest } from '@server/tests/shared' | 8 | import { makePOSTAPRequest } from '@server/tests/shared' |
9 | import { buildAbsoluteFixturePath, wait } from '@shared/core-utils' | 9 | import { buildAbsoluteFixturePath, wait } from '@shared/core-utils' |
10 | import { HttpStatusCode } from '@shared/models' | 10 | import { HttpStatusCode } from '@shared/models' |
11 | import { cleanupTests, createMultipleServers, killallServers, PeerTubeServer } from '@shared/server-commands' | 11 | import { 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 | ||
46 | async 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 | |||
46 | describe('Test ActivityPub security', function () { | 72 | describe('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 @@ | |||
1 | import { buildDigest } from '@server/helpers/peertube-crypto' | ||
2 | import { doRequest } from '@server/helpers/requests' | 1 | import { doRequest } from '@server/helpers/requests' |
3 | import { ACTIVITY_PUB, HTTP_SIGNATURE } from '@server/initializers/constants' | ||
4 | import { activityPubContextify } from '@server/lib/activitypub/context' | ||
5 | 2 | ||
6 | export function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) { | 3 | export 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 | |||
17 | export 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 | ||
78 | export const POSSIBLE_LOCALES = Object.keys(I18N_LOCALES) | 78 | export const POSSIBLE_LOCALES = (Object.keys(I18N_LOCALES) as string[]).concat(Object.keys(I18N_LOCALE_ALIAS)) |
79 | .concat(Object.keys(I18N_LOCALE_ALIAS)) | ||
80 | 79 | ||
81 | export function getDefaultLocale () { | 80 | export 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 | ||
119 | export type ServerFilterHookName = keyof typeof serverFilterHookObject | 125 | export type ServerFilterHookName = keyof typeof serverFilterHookObject |