diff options
28 files changed, 514 insertions, 286 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index 99637dbab..d0f761009 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts | |||
@@ -186,35 +186,35 @@ export { | |||
186 | function accountController (req: express.Request, res: express.Response) { | 186 | function accountController (req: express.Request, res: express.Response) { |
187 | const account = res.locals.account | 187 | const account = res.locals.account |
188 | 188 | ||
189 | return activityPubResponse(activityPubContextify(account.toActivityPubObject()), res) | 189 | return activityPubResponse(activityPubContextify(account.toActivityPubObject(), 'Actor'), res) |
190 | } | 190 | } |
191 | 191 | ||
192 | async function accountFollowersController (req: express.Request, res: express.Response) { | 192 | async function accountFollowersController (req: express.Request, res: express.Response) { |
193 | const account = res.locals.account | 193 | const account = res.locals.account |
194 | const activityPubResult = await actorFollowers(req, account.Actor) | 194 | const activityPubResult = await actorFollowers(req, account.Actor) |
195 | 195 | ||
196 | return activityPubResponse(activityPubContextify(activityPubResult), res) | 196 | return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res) |
197 | } | 197 | } |
198 | 198 | ||
199 | async function accountFollowingController (req: express.Request, res: express.Response) { | 199 | async function accountFollowingController (req: express.Request, res: express.Response) { |
200 | const account = res.locals.account | 200 | const account = res.locals.account |
201 | const activityPubResult = await actorFollowing(req, account.Actor) | 201 | const activityPubResult = await actorFollowing(req, account.Actor) |
202 | 202 | ||
203 | return activityPubResponse(activityPubContextify(activityPubResult), res) | 203 | return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res) |
204 | } | 204 | } |
205 | 205 | ||
206 | async function accountPlaylistsController (req: express.Request, res: express.Response) { | 206 | async function accountPlaylistsController (req: express.Request, res: express.Response) { |
207 | const account = res.locals.account | 207 | const account = res.locals.account |
208 | const activityPubResult = await actorPlaylists(req, { account }) | 208 | const activityPubResult = await actorPlaylists(req, { account }) |
209 | 209 | ||
210 | return activityPubResponse(activityPubContextify(activityPubResult), res) | 210 | return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res) |
211 | } | 211 | } |
212 | 212 | ||
213 | async function videoChannelPlaylistsController (req: express.Request, res: express.Response) { | 213 | async function videoChannelPlaylistsController (req: express.Request, res: express.Response) { |
214 | const channel = res.locals.videoChannel | 214 | const channel = res.locals.videoChannel |
215 | const activityPubResult = await actorPlaylists(req, { channel }) | 215 | const activityPubResult = await actorPlaylists(req, { channel }) |
216 | 216 | ||
217 | return activityPubResponse(activityPubContextify(activityPubResult), res) | 217 | return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res) |
218 | } | 218 | } |
219 | 219 | ||
220 | function getAccountVideoRateFactory (rateType: VideoRateType) { | 220 | function getAccountVideoRateFactory (rateType: VideoRateType) { |
@@ -226,7 +226,7 @@ function getAccountVideoRateFactory (rateType: VideoRateType) { | |||
226 | ? buildLikeActivity(accountVideoRate.url, byActor, accountVideoRate.Video) | 226 | ? buildLikeActivity(accountVideoRate.url, byActor, accountVideoRate.Video) |
227 | : buildDislikeActivity(accountVideoRate.url, byActor, accountVideoRate.Video) | 227 | : buildDislikeActivity(accountVideoRate.url, byActor, accountVideoRate.Video) |
228 | 228 | ||
229 | return activityPubResponse(activityPubContextify(APObject), res) | 229 | return activityPubResponse(activityPubContextify(APObject, 'Rate'), res) |
230 | } | 230 | } |
231 | } | 231 | } |
232 | 232 | ||
@@ -244,10 +244,10 @@ async function videoController (req: express.Request, res: express.Response) { | |||
244 | 244 | ||
245 | if (req.path.endsWith('/activity')) { | 245 | if (req.path.endsWith('/activity')) { |
246 | const data = buildCreateActivity(videoWithCaptions.url, video.VideoChannel.Account.Actor, videoObject, audience) | 246 | const data = buildCreateActivity(videoWithCaptions.url, video.VideoChannel.Account.Actor, videoObject, audience) |
247 | return activityPubResponse(activityPubContextify(data), res) | 247 | return activityPubResponse(activityPubContextify(data, 'Video'), res) |
248 | } | 248 | } |
249 | 249 | ||
250 | return activityPubResponse(activityPubContextify(videoObject), res) | 250 | return activityPubResponse(activityPubContextify(videoObject, 'Video'), res) |
251 | } | 251 | } |
252 | 252 | ||
253 | async function videoAnnounceController (req: express.Request, res: express.Response) { | 253 | async function videoAnnounceController (req: express.Request, res: express.Response) { |
@@ -274,7 +274,7 @@ async function videoAnnouncesController (req: express.Request, res: express.Resp | |||
274 | } | 274 | } |
275 | const json = await activityPubCollectionPagination(getLocalVideoSharesActivityPubUrl(video), handler, req.query.page) | 275 | const json = await activityPubCollectionPagination(getLocalVideoSharesActivityPubUrl(video), handler, req.query.page) |
276 | 276 | ||
277 | return activityPubResponse(activityPubContextify(json), res) | 277 | return activityPubResponse(activityPubContextify(json, 'Collection'), res) |
278 | } | 278 | } |
279 | 279 | ||
280 | async function videoLikesController (req: express.Request, res: express.Response) { | 280 | async function videoLikesController (req: express.Request, res: express.Response) { |
@@ -284,7 +284,7 @@ async function videoLikesController (req: express.Request, res: express.Response | |||
284 | 284 | ||
285 | const json = await videoRates(req, 'like', video, getLocalVideoLikesActivityPubUrl(video)) | 285 | const json = await videoRates(req, 'like', video, getLocalVideoLikesActivityPubUrl(video)) |
286 | 286 | ||
287 | return activityPubResponse(activityPubContextify(json), res) | 287 | return activityPubResponse(activityPubContextify(json, 'Collection'), res) |
288 | } | 288 | } |
289 | 289 | ||
290 | async function videoDislikesController (req: express.Request, res: express.Response) { | 290 | async function videoDislikesController (req: express.Request, res: express.Response) { |
@@ -294,7 +294,7 @@ async function videoDislikesController (req: express.Request, res: express.Respo | |||
294 | 294 | ||
295 | const json = await videoRates(req, 'dislike', video, getLocalVideoDislikesActivityPubUrl(video)) | 295 | const json = await videoRates(req, 'dislike', video, getLocalVideoDislikesActivityPubUrl(video)) |
296 | 296 | ||
297 | return activityPubResponse(activityPubContextify(json), res) | 297 | return activityPubResponse(activityPubContextify(json, 'Collection'), res) |
298 | } | 298 | } |
299 | 299 | ||
300 | async function videoCommentsController (req: express.Request, res: express.Response) { | 300 | async function videoCommentsController (req: express.Request, res: express.Response) { |
@@ -312,27 +312,27 @@ async function videoCommentsController (req: express.Request, res: express.Respo | |||
312 | } | 312 | } |
313 | const json = await activityPubCollectionPagination(getLocalVideoCommentsActivityPubUrl(video), handler, req.query.page) | 313 | const json = await activityPubCollectionPagination(getLocalVideoCommentsActivityPubUrl(video), handler, req.query.page) |
314 | 314 | ||
315 | return activityPubResponse(activityPubContextify(json), res) | 315 | return activityPubResponse(activityPubContextify(json, 'Collection'), res) |
316 | } | 316 | } |
317 | 317 | ||
318 | function videoChannelController (req: express.Request, res: express.Response) { | 318 | function videoChannelController (req: express.Request, res: express.Response) { |
319 | const videoChannel = res.locals.videoChannel | 319 | const videoChannel = res.locals.videoChannel |
320 | 320 | ||
321 | return activityPubResponse(activityPubContextify(videoChannel.toActivityPubObject()), res) | 321 | return activityPubResponse(activityPubContextify(videoChannel.toActivityPubObject(), 'Actor'), res) |
322 | } | 322 | } |
323 | 323 | ||
324 | async function videoChannelFollowersController (req: express.Request, res: express.Response) { | 324 | async function videoChannelFollowersController (req: express.Request, res: express.Response) { |
325 | const videoChannel = res.locals.videoChannel | 325 | const videoChannel = res.locals.videoChannel |
326 | const activityPubResult = await actorFollowers(req, videoChannel.Actor) | 326 | const activityPubResult = await actorFollowers(req, videoChannel.Actor) |
327 | 327 | ||
328 | return activityPubResponse(activityPubContextify(activityPubResult), res) | 328 | return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res) |
329 | } | 329 | } |
330 | 330 | ||
331 | async function videoChannelFollowingController (req: express.Request, res: express.Response) { | 331 | async function videoChannelFollowingController (req: express.Request, res: express.Response) { |
332 | const videoChannel = res.locals.videoChannel | 332 | const videoChannel = res.locals.videoChannel |
333 | const activityPubResult = await actorFollowing(req, videoChannel.Actor) | 333 | const activityPubResult = await actorFollowing(req, videoChannel.Actor) |
334 | 334 | ||
335 | return activityPubResponse(activityPubContextify(activityPubResult), res) | 335 | return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res) |
336 | } | 336 | } |
337 | 337 | ||
338 | async function videoCommentController (req: express.Request, res: express.Response) { | 338 | async function videoCommentController (req: express.Request, res: express.Response) { |
@@ -350,11 +350,11 @@ async function videoCommentController (req: express.Request, res: express.Respon | |||
350 | 350 | ||
351 | if (req.path.endsWith('/activity')) { | 351 | if (req.path.endsWith('/activity')) { |
352 | const data = buildCreateActivity(videoComment.url, videoComment.Account.Actor, videoCommentObject, audience) | 352 | const data = buildCreateActivity(videoComment.url, videoComment.Account.Actor, videoCommentObject, audience) |
353 | return activityPubResponse(activityPubContextify(data), res) | 353 | return activityPubResponse(activityPubContextify(data, 'Comment'), res) |
354 | } | 354 | } |
355 | } | 355 | } |
356 | 356 | ||
357 | return activityPubResponse(activityPubContextify(videoCommentObject), res) | 357 | return activityPubResponse(activityPubContextify(videoCommentObject, 'Comment'), res) |
358 | } | 358 | } |
359 | 359 | ||
360 | async function videoRedundancyController (req: express.Request, res: express.Response) { | 360 | async function videoRedundancyController (req: express.Request, res: express.Response) { |
@@ -387,7 +387,7 @@ async function videoPlaylistController (req: express.Request, res: express.Respo | |||
387 | const audience = getAudience(playlist.OwnerAccount.Actor, playlist.privacy === VideoPlaylistPrivacy.PUBLIC) | 387 | const audience = getAudience(playlist.OwnerAccount.Actor, playlist.privacy === VideoPlaylistPrivacy.PUBLIC) |
388 | const object = audiencify(json, audience) | 388 | const object = audiencify(json, audience) |
389 | 389 | ||
390 | return activityPubResponse(activityPubContextify(object), res) | 390 | return activityPubResponse(activityPubContextify(object, 'Playlist'), res) |
391 | } | 391 | } |
392 | 392 | ||
393 | function videoPlaylistElementController (req: express.Request, res: express.Response) { | 393 | function videoPlaylistElementController (req: express.Request, res: express.Response) { |
@@ -396,7 +396,7 @@ function videoPlaylistElementController (req: express.Request, res: express.Resp | |||
396 | if (redirectIfNotOwned(videoPlaylistElement.url, res)) return | 396 | if (redirectIfNotOwned(videoPlaylistElement.url, res)) return |
397 | 397 | ||
398 | const json = videoPlaylistElement.toActivityPubObject() | 398 | const json = videoPlaylistElement.toActivityPubObject() |
399 | return activityPubResponse(activityPubContextify(json), res) | 399 | return activityPubResponse(activityPubContextify(json, 'Playlist'), res) |
400 | } | 400 | } |
401 | 401 | ||
402 | // --------------------------------------------------------------------------- | 402 | // --------------------------------------------------------------------------- |
diff --git a/server/controllers/activitypub/outbox.ts b/server/controllers/activitypub/outbox.ts index 4e7a3afeb..f385c9927 100644 --- a/server/controllers/activitypub/outbox.ts +++ b/server/controllers/activitypub/outbox.ts | |||
@@ -45,7 +45,7 @@ async function outboxController (req: express.Request, res: express.Response) { | |||
45 | const handler = (start: number, count: number) => buildActivities(actor, start, count) | 45 | const handler = (start: number, count: number) => buildActivities(actor, start, count) |
46 | const json = await activityPubCollectionPagination(actorOutboxUrl, handler, req.query.page, req.query.size) | 46 | const json = await activityPubCollectionPagination(actorOutboxUrl, handler, req.query.page, req.query.size) |
47 | 47 | ||
48 | return activityPubResponse(activityPubContextify(json), res) | 48 | return activityPubResponse(activityPubContextify(json, 'Collection'), res) |
49 | } | 49 | } |
50 | 50 | ||
51 | async function buildActivities (actor: MActorLight, start: number, count: number) { | 51 | async function buildActivities (actor: MActorLight, start: number, count: number) { |
diff --git a/server/lib/activitypub/activity.ts b/server/lib/activitypub/activity.ts index 215b50b69..cccb7b1c1 100644 --- a/server/lib/activitypub/activity.ts +++ b/server/lib/activitypub/activity.ts | |||
@@ -1,14 +1,3 @@ | |||
1 | import { signJsonLDObject } from '@server/helpers/peertube-crypto' | ||
2 | import { MActor } from '@server/types/models' | ||
3 | import { ContextType } from '@shared/models' | ||
4 | import { activityPubContextify } from './context' | ||
5 | |||
6 | function buildSignedActivity <T> (byActor: MActor, data: T, contextType?: ContextType) { | ||
7 | const activity = activityPubContextify(data, contextType) | ||
8 | |||
9 | return signJsonLDObject(byActor, activity) | ||
10 | } | ||
11 | |||
12 | function getAPId (object: string | { id: string }) { | 1 | function getAPId (object: string | { id: string }) { |
13 | if (typeof object === 'string') return object | 2 | if (typeof object === 'string') return object |
14 | 3 | ||
@@ -16,6 +5,5 @@ function getAPId (object: string | { id: string }) { | |||
16 | } | 5 | } |
17 | 6 | ||
18 | export { | 7 | export { |
19 | buildSignedActivity, | ||
20 | getAPId | 8 | getAPId |
21 | } | 9 | } |
diff --git a/server/lib/activitypub/audience.ts b/server/lib/activitypub/audience.ts index 2bd5bb066..6f5491387 100644 --- a/server/lib/activitypub/audience.ts +++ b/server/lib/activitypub/audience.ts | |||
@@ -22,7 +22,7 @@ function buildAudience (followerUrls: string[], isPublic = true) { | |||
22 | } | 22 | } |
23 | 23 | ||
24 | function audiencify<T> (object: T, audience: ActivityAudience) { | 24 | function audiencify<T> (object: T, audience: ActivityAudience) { |
25 | return Object.assign(object, audience) | 25 | return { ...audience, ...object } |
26 | } | 26 | } |
27 | 27 | ||
28 | // --------------------------------------------------------------------------- | 28 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/activitypub/context.ts b/server/lib/activitypub/context.ts index 71f08da80..3bc40e2aa 100644 --- a/server/lib/activitypub/context.ts +++ b/server/lib/activitypub/context.ts | |||
@@ -1,137 +1,168 @@ | |||
1 | import { ContextType } from '@shared/models' | 1 | import { ContextType } from '@shared/models' |
2 | 2 | ||
3 | function getContextData (type: ContextType) { | 3 | function activityPubContextify <T> (data: T, type: ContextType) { |
4 | const context: any[] = [ | 4 | return { ...getContextData(type), ...data } |
5 | 'https://www.w3.org/ns/activitystreams', | 5 | } |
6 | 'https://w3id.org/security/v1', | 6 | |
7 | { | 7 | // --------------------------------------------------------------------------- |
8 | RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' | 8 | |
9 | export { | ||
10 | getContextData, | ||
11 | activityPubContextify | ||
12 | } | ||
13 | |||
14 | // --------------------------------------------------------------------------- | ||
15 | |||
16 | type ContextValue = { [ id: string ]: (string | { '@type': string, '@id': string }) } | ||
17 | |||
18 | const contextStore = { | ||
19 | Video: buildContext({ | ||
20 | Hashtag: 'as:Hashtag', | ||
21 | uuid: 'sc:identifier', | ||
22 | category: 'sc:category', | ||
23 | licence: 'sc:license', | ||
24 | subtitleLanguage: 'sc:subtitleLanguage', | ||
25 | sensitive: 'as:sensitive', | ||
26 | language: 'sc:inLanguage', | ||
27 | |||
28 | // TODO: remove in a few versions, introduced in 4.2 | ||
29 | icons: 'as:icon', | ||
30 | |||
31 | isLiveBroadcast: 'sc:isLiveBroadcast', | ||
32 | liveSaveReplay: { | ||
33 | '@type': 'sc:Boolean', | ||
34 | '@id': 'pt:liveSaveReplay' | ||
35 | }, | ||
36 | permanentLive: { | ||
37 | '@type': 'sc:Boolean', | ||
38 | '@id': 'pt:permanentLive' | ||
39 | }, | ||
40 | latencyMode: { | ||
41 | '@type': 'sc:Number', | ||
42 | '@id': 'pt:latencyMode' | ||
43 | }, | ||
44 | |||
45 | Infohash: 'pt:Infohash', | ||
46 | |||
47 | originallyPublishedAt: 'sc:datePublished', | ||
48 | views: { | ||
49 | '@type': 'sc:Number', | ||
50 | '@id': 'pt:views' | ||
51 | }, | ||
52 | state: { | ||
53 | '@type': 'sc:Number', | ||
54 | '@id': 'pt:state' | ||
55 | }, | ||
56 | size: { | ||
57 | '@type': 'sc:Number', | ||
58 | '@id': 'pt:size' | ||
59 | }, | ||
60 | fps: { | ||
61 | '@type': 'sc:Number', | ||
62 | '@id': 'pt:fps' | ||
63 | }, | ||
64 | commentsEnabled: { | ||
65 | '@type': 'sc:Boolean', | ||
66 | '@id': 'pt:commentsEnabled' | ||
67 | }, | ||
68 | downloadEnabled: { | ||
69 | '@type': 'sc:Boolean', | ||
70 | '@id': 'pt:downloadEnabled' | ||
71 | }, | ||
72 | waitTranscoding: { | ||
73 | '@type': 'sc:Boolean', | ||
74 | '@id': 'pt:waitTranscoding' | ||
75 | }, | ||
76 | support: { | ||
77 | '@type': 'sc:Text', | ||
78 | '@id': 'pt:support' | ||
79 | }, | ||
80 | likes: { | ||
81 | '@id': 'as:likes', | ||
82 | '@type': '@id' | ||
83 | }, | ||
84 | dislikes: { | ||
85 | '@id': 'as:dislikes', | ||
86 | '@type': '@id' | ||
87 | }, | ||
88 | shares: { | ||
89 | '@id': 'as:shares', | ||
90 | '@type': '@id' | ||
91 | }, | ||
92 | comments: { | ||
93 | '@id': 'as:comments', | ||
94 | '@type': '@id' | ||
9 | } | 95 | } |
10 | ] | 96 | }), |
11 | 97 | ||
12 | if (type !== 'View' && type !== 'Announce') { | 98 | Playlist: buildContext({ |
13 | const additional = { | 99 | Playlist: 'pt:Playlist', |
14 | pt: 'https://joinpeertube.org/ns#', | 100 | PlaylistElement: 'pt:PlaylistElement', |
15 | sc: 'http://schema.org#' | 101 | position: { |
102 | '@type': 'sc:Number', | ||
103 | '@id': 'pt:position' | ||
104 | }, | ||
105 | startTimestamp: { | ||
106 | '@type': 'sc:Number', | ||
107 | '@id': 'pt:startTimestamp' | ||
108 | }, | ||
109 | stopTimestamp: { | ||
110 | '@type': 'sc:Number', | ||
111 | '@id': 'pt:stopTimestamp' | ||
16 | } | 112 | } |
113 | }), | ||
114 | |||
115 | CacheFile: buildContext({ | ||
116 | expires: 'sc:expires', | ||
117 | CacheFile: 'pt:CacheFile' | ||
118 | }), | ||
17 | 119 | ||
18 | if (type === 'CacheFile') { | 120 | Flag: buildContext({ |
19 | Object.assign(additional, { | 121 | Hashtag: 'as:Hashtag' |
20 | expires: 'sc:expires', | 122 | }), |
21 | CacheFile: 'pt:CacheFile' | 123 | |
22 | }) | 124 | Actor: buildContext({ |
23 | } else { | 125 | playlists: { |
24 | Object.assign(additional, { | 126 | '@id': 'pt:playlists', |
25 | Hashtag: 'as:Hashtag', | 127 | '@type': '@id' |
26 | uuid: 'sc:identifier', | ||
27 | category: 'sc:category', | ||
28 | licence: 'sc:license', | ||
29 | subtitleLanguage: 'sc:subtitleLanguage', | ||
30 | sensitive: 'as:sensitive', | ||
31 | language: 'sc:inLanguage', | ||
32 | |||
33 | // TODO: remove in a few versions, introduced in 4.2 | ||
34 | icons: 'as:icon', | ||
35 | |||
36 | isLiveBroadcast: 'sc:isLiveBroadcast', | ||
37 | liveSaveReplay: { | ||
38 | '@type': 'sc:Boolean', | ||
39 | '@id': 'pt:liveSaveReplay' | ||
40 | }, | ||
41 | permanentLive: { | ||
42 | '@type': 'sc:Boolean', | ||
43 | '@id': 'pt:permanentLive' | ||
44 | }, | ||
45 | latencyMode: { | ||
46 | '@type': 'sc:Number', | ||
47 | '@id': 'pt:latencyMode' | ||
48 | }, | ||
49 | |||
50 | Infohash: 'pt:Infohash', | ||
51 | Playlist: 'pt:Playlist', | ||
52 | PlaylistElement: 'pt:PlaylistElement', | ||
53 | |||
54 | originallyPublishedAt: 'sc:datePublished', | ||
55 | views: { | ||
56 | '@type': 'sc:Number', | ||
57 | '@id': 'pt:views' | ||
58 | }, | ||
59 | state: { | ||
60 | '@type': 'sc:Number', | ||
61 | '@id': 'pt:state' | ||
62 | }, | ||
63 | size: { | ||
64 | '@type': 'sc:Number', | ||
65 | '@id': 'pt:size' | ||
66 | }, | ||
67 | fps: { | ||
68 | '@type': 'sc:Number', | ||
69 | '@id': 'pt:fps' | ||
70 | }, | ||
71 | startTimestamp: { | ||
72 | '@type': 'sc:Number', | ||
73 | '@id': 'pt:startTimestamp' | ||
74 | }, | ||
75 | stopTimestamp: { | ||
76 | '@type': 'sc:Number', | ||
77 | '@id': 'pt:stopTimestamp' | ||
78 | }, | ||
79 | position: { | ||
80 | '@type': 'sc:Number', | ||
81 | '@id': 'pt:position' | ||
82 | }, | ||
83 | commentsEnabled: { | ||
84 | '@type': 'sc:Boolean', | ||
85 | '@id': 'pt:commentsEnabled' | ||
86 | }, | ||
87 | downloadEnabled: { | ||
88 | '@type': 'sc:Boolean', | ||
89 | '@id': 'pt:downloadEnabled' | ||
90 | }, | ||
91 | waitTranscoding: { | ||
92 | '@type': 'sc:Boolean', | ||
93 | '@id': 'pt:waitTranscoding' | ||
94 | }, | ||
95 | support: { | ||
96 | '@type': 'sc:Text', | ||
97 | '@id': 'pt:support' | ||
98 | }, | ||
99 | likes: { | ||
100 | '@id': 'as:likes', | ||
101 | '@type': '@id' | ||
102 | }, | ||
103 | dislikes: { | ||
104 | '@id': 'as:dislikes', | ||
105 | '@type': '@id' | ||
106 | }, | ||
107 | playlists: { | ||
108 | '@id': 'pt:playlists', | ||
109 | '@type': '@id' | ||
110 | }, | ||
111 | shares: { | ||
112 | '@id': 'as:shares', | ||
113 | '@type': '@id' | ||
114 | }, | ||
115 | comments: { | ||
116 | '@id': 'as:comments', | ||
117 | '@type': '@id' | ||
118 | } | ||
119 | }) | ||
120 | } | 128 | } |
129 | }), | ||
121 | 130 | ||
122 | context.push(additional) | 131 | Follow: buildContext(), |
123 | } | 132 | Reject: buildContext(), |
133 | Accept: buildContext(), | ||
134 | View: buildContext(), | ||
135 | Announce: buildContext(), | ||
136 | Comment: buildContext(), | ||
137 | Delete: buildContext(), | ||
138 | Rate: buildContext() | ||
139 | } | ||
124 | 140 | ||
141 | function getContextData (type: ContextType) { | ||
125 | return { | 142 | return { |
126 | '@context': context | 143 | '@context': contextStore[type] |
127 | } | 144 | } |
128 | } | 145 | } |
129 | 146 | ||
130 | function activityPubContextify <T> (data: T, type: ContextType = 'All') { | 147 | function buildContext (contextValue?: ContextValue) { |
131 | return Object.assign({}, data, getContextData(type)) | 148 | const baseContext = [ |
132 | } | 149 | 'https://www.w3.org/ns/activitystreams', |
150 | 'https://w3id.org/security/v1', | ||
151 | { | ||
152 | RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' | ||
153 | } | ||
154 | ] | ||
133 | 155 | ||
134 | export { | 156 | if (!contextValue) return baseContext |
135 | getContextData, | 157 | |
136 | activityPubContextify | 158 | return [ |
159 | ...baseContext, | ||
160 | |||
161 | { | ||
162 | pt: 'https://joinpeertube.org/ns#', | ||
163 | sc: 'http://schema.org#', | ||
164 | |||
165 | ...contextValue | ||
166 | } | ||
167 | ] | ||
137 | } | 168 | } |
diff --git a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts b/server/lib/activitypub/send/http.ts index 2a03325b7..d8d0b8542 100644 --- a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts +++ b/server/lib/activitypub/send/http.ts | |||
@@ -1,12 +1,12 @@ | |||
1 | import { buildDigest } from '@server/helpers/peertube-crypto' | 1 | import { buildDigest, signJsonLDObject } from '@server/helpers/peertube-crypto' |
2 | import { buildSignedActivity } from '@server/lib/activitypub/activity' | 2 | import { ACTIVITY_PUB, HTTP_SIGNATURE } from '@server/initializers/constants' |
3 | import { ActorModel } from '@server/models/actor/actor' | ||
3 | import { getServerActor } from '@server/models/application/application' | 4 | import { getServerActor } from '@server/models/application/application' |
5 | import { MActor } from '@server/types/models' | ||
4 | import { ContextType } from '@shared/models/activitypub/context' | 6 | import { ContextType } from '@shared/models/activitypub/context' |
5 | import { ACTIVITY_PUB, HTTP_SIGNATURE } from '../../../../initializers/constants' | 7 | import { activityPubContextify } from '../context' |
6 | import { ActorModel } from '../../../../models/actor/actor' | ||
7 | import { MActor } from '../../../../types/models' | ||
8 | 8 | ||
9 | type Payload <T> = { body: T, contextType?: ContextType, signatureActorId?: number } | 9 | type Payload <T> = { body: T, contextType: ContextType, signatureActorId?: number } |
10 | 10 | ||
11 | async function computeBody <T> ( | 11 | async function computeBody <T> ( |
12 | payload: Payload<T> | 12 | payload: Payload<T> |
@@ -17,7 +17,7 @@ async function computeBody <T> ( | |||
17 | const actorSignature = await ActorModel.load(payload.signatureActorId) | 17 | const actorSignature = await ActorModel.load(payload.signatureActorId) |
18 | if (!actorSignature) throw new Error('Unknown signature actor id.') | 18 | if (!actorSignature) throw new Error('Unknown signature actor id.') |
19 | 19 | ||
20 | body = await buildSignedActivity(actorSignature, payload.body, payload.contextType) | 20 | body = await signAndContextify(actorSignature, payload.body, payload.contextType) |
21 | } | 21 | } |
22 | 22 | ||
23 | return body | 23 | return body |
@@ -52,8 +52,17 @@ function buildGlobalHeaders (body: any) { | |||
52 | } | 52 | } |
53 | } | 53 | } |
54 | 54 | ||
55 | function signAndContextify <T> (byActor: MActor, data: T, contextType: ContextType | null) { | ||
56 | const activity = contextType | ||
57 | ? activityPubContextify(data, contextType) | ||
58 | : data | ||
59 | |||
60 | return signJsonLDObject(byActor, activity) | ||
61 | } | ||
62 | |||
55 | export { | 63 | export { |
56 | buildGlobalHeaders, | 64 | buildGlobalHeaders, |
57 | computeBody, | 65 | computeBody, |
58 | buildSignedRequestOptions | 66 | buildSignedRequestOptions, |
67 | signAndContextify | ||
59 | } | 68 | } |
diff --git a/server/lib/activitypub/send/index.ts b/server/lib/activitypub/send/index.ts index 028936810..852ea2e74 100644 --- a/server/lib/activitypub/send/index.ts +++ b/server/lib/activitypub/send/index.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | export * from './send-accept' | 1 | export * from './http' |
2 | export * from './send-accept' | 2 | export * from './send-accept' |
3 | export * from './send-announce' | 3 | export * from './send-announce' |
4 | export * from './send-create' | 4 | export * from './send-create' |
diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts index 939f06d9e..4c9bcbb0b 100644 --- a/server/lib/activitypub/send/send-accept.ts +++ b/server/lib/activitypub/send/send-accept.ts | |||
@@ -21,7 +21,12 @@ function sendAccept (actorFollow: MActorFollowActors) { | |||
21 | const url = getLocalActorFollowAcceptActivityPubUrl(actorFollow) | 21 | const url = getLocalActorFollowAcceptActivityPubUrl(actorFollow) |
22 | const data = buildAcceptActivity(url, me, followData) | 22 | const data = buildAcceptActivity(url, me, followData) |
23 | 23 | ||
24 | return unicastTo(data, me, follower.inboxUrl) | 24 | return unicastTo({ |
25 | data, | ||
26 | byActor: me, | ||
27 | toActorUrl: follower.inboxUrl, | ||
28 | contextType: 'Accept' | ||
29 | }) | ||
25 | } | 30 | } |
26 | 31 | ||
27 | // --------------------------------------------------------------------------- | 32 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts index 7897beb75..6c078b047 100644 --- a/server/lib/activitypub/send/send-announce.ts +++ b/server/lib/activitypub/send/send-announce.ts | |||
@@ -23,13 +23,19 @@ async function buildAnnounceWithVideoAudience ( | |||
23 | return { activity, actorsInvolvedInVideo } | 23 | return { activity, actorsInvolvedInVideo } |
24 | } | 24 | } |
25 | 25 | ||
26 | async function sendVideoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) { | 26 | async function sendVideoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, transaction: Transaction) { |
27 | const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t) | 27 | const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, transaction) |
28 | 28 | ||
29 | logger.info('Creating job to send announce %s.', videoShare.url) | 29 | logger.info('Creating job to send announce %s.', videoShare.url) |
30 | 30 | ||
31 | const followersException = [ byActor ] | 31 | return broadcastToFollowers({ |
32 | return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, t, followersException, 'Announce') | 32 | data: activity, |
33 | byActor, | ||
34 | toFollowersOf: actorsInvolvedInVideo, | ||
35 | transaction, | ||
36 | actorsException: [ byActor ], | ||
37 | contextType: 'Announce' | ||
38 | }) | ||
33 | } | 39 | } |
34 | 40 | ||
35 | function buildAnnounceActivity (url: string, byActor: MActorLight, object: string, audience?: ActivityAudience): ActivityAnnounce { | 41 | function buildAnnounceActivity (url: string, byActor: MActorLight, object: string, audience?: ActivityAudience): ActivityAnnounce { |
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index f6d897220..5d8763495 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts | |||
@@ -25,7 +25,7 @@ import { | |||
25 | 25 | ||
26 | const lTags = loggerTagsFactory('ap', 'create') | 26 | const lTags = loggerTagsFactory('ap', 'create') |
27 | 27 | ||
28 | async function sendCreateVideo (video: MVideoAP, t: Transaction) { | 28 | async function sendCreateVideo (video: MVideoAP, transaction: Transaction) { |
29 | if (!video.hasPrivacyForFederation()) return undefined | 29 | if (!video.hasPrivacyForFederation()) return undefined |
30 | 30 | ||
31 | logger.info('Creating job to send video creation of %s.', video.url, lTags(video.uuid)) | 31 | logger.info('Creating job to send video creation of %s.', video.url, lTags(video.uuid)) |
@@ -36,7 +36,13 @@ async function sendCreateVideo (video: MVideoAP, t: Transaction) { | |||
36 | const audience = getAudience(byActor, video.privacy === VideoPrivacy.PUBLIC) | 36 | const audience = getAudience(byActor, video.privacy === VideoPrivacy.PUBLIC) |
37 | const createActivity = buildCreateActivity(video.url, byActor, videoObject, audience) | 37 | const createActivity = buildCreateActivity(video.url, byActor, videoObject, audience) |
38 | 38 | ||
39 | return broadcastToFollowers(createActivity, byActor, [ byActor ], t) | 39 | return broadcastToFollowers({ |
40 | data: createActivity, | ||
41 | byActor, | ||
42 | toFollowersOf: [ byActor ], | ||
43 | transaction, | ||
44 | contextType: 'Video' | ||
45 | }) | ||
40 | } | 46 | } |
41 | 47 | ||
42 | async function sendCreateCacheFile ( | 48 | async function sendCreateCacheFile ( |
@@ -55,7 +61,7 @@ async function sendCreateCacheFile ( | |||
55 | }) | 61 | }) |
56 | } | 62 | } |
57 | 63 | ||
58 | async function sendCreateVideoPlaylist (playlist: MVideoPlaylistFull, t: Transaction) { | 64 | async function sendCreateVideoPlaylist (playlist: MVideoPlaylistFull, transaction: Transaction) { |
59 | if (playlist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined | 65 | if (playlist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined |
60 | 66 | ||
61 | logger.info('Creating job to send create video playlist of %s.', playlist.url, lTags(playlist.uuid)) | 67 | logger.info('Creating job to send create video playlist of %s.', playlist.url, lTags(playlist.uuid)) |
@@ -63,7 +69,7 @@ async function sendCreateVideoPlaylist (playlist: MVideoPlaylistFull, t: Transac | |||
63 | const byActor = playlist.OwnerAccount.Actor | 69 | const byActor = playlist.OwnerAccount.Actor |
64 | const audience = getAudience(byActor, playlist.privacy === VideoPlaylistPrivacy.PUBLIC) | 70 | const audience = getAudience(byActor, playlist.privacy === VideoPlaylistPrivacy.PUBLIC) |
65 | 71 | ||
66 | const object = await playlist.toActivityPubObject(null, t) | 72 | const object = await playlist.toActivityPubObject(null, transaction) |
67 | const createActivity = buildCreateActivity(playlist.url, byActor, object, audience) | 73 | const createActivity = buildCreateActivity(playlist.url, byActor, object, audience) |
68 | 74 | ||
69 | const serverActor = await getServerActor() | 75 | const serverActor = await getServerActor() |
@@ -71,19 +77,25 @@ async function sendCreateVideoPlaylist (playlist: MVideoPlaylistFull, t: Transac | |||
71 | 77 | ||
72 | if (playlist.VideoChannel) toFollowersOf.push(playlist.VideoChannel.Actor) | 78 | if (playlist.VideoChannel) toFollowersOf.push(playlist.VideoChannel.Actor) |
73 | 79 | ||
74 | return broadcastToFollowers(createActivity, byActor, toFollowersOf, t) | 80 | return broadcastToFollowers({ |
81 | data: createActivity, | ||
82 | byActor, | ||
83 | toFollowersOf, | ||
84 | transaction, | ||
85 | contextType: 'Playlist' | ||
86 | }) | ||
75 | } | 87 | } |
76 | 88 | ||
77 | async function sendCreateVideoComment (comment: MCommentOwnerVideo, t: Transaction) { | 89 | async function sendCreateVideoComment (comment: MCommentOwnerVideo, transaction: Transaction) { |
78 | logger.info('Creating job to send comment %s.', comment.url) | 90 | logger.info('Creating job to send comment %s.', comment.url) |
79 | 91 | ||
80 | const isOrigin = comment.Video.isOwned() | 92 | const isOrigin = comment.Video.isOwned() |
81 | 93 | ||
82 | const byActor = comment.Account.Actor | 94 | const byActor = comment.Account.Actor |
83 | const threadParentComments = await VideoCommentModel.listThreadParentComments(comment, t) | 95 | const threadParentComments = await VideoCommentModel.listThreadParentComments(comment, transaction) |
84 | const commentObject = comment.toActivityPubObject(threadParentComments) | 96 | const commentObject = comment.toActivityPubObject(threadParentComments) |
85 | 97 | ||
86 | const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t) | 98 | const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, transaction) |
87 | // Add the actor that commented too | 99 | // Add the actor that commented too |
88 | actorsInvolvedInComment.push(byActor) | 100 | actorsInvolvedInComment.push(byActor) |
89 | 101 | ||
@@ -101,16 +113,45 @@ async function sendCreateVideoComment (comment: MCommentOwnerVideo, t: Transacti | |||
101 | 113 | ||
102 | // This was a reply, send it to the parent actors | 114 | // This was a reply, send it to the parent actors |
103 | const actorsException = [ byActor ] | 115 | const actorsException = [ byActor ] |
104 | await broadcastToActors(createActivity, byActor, parentsCommentActors, t, actorsException) | 116 | await broadcastToActors({ |
117 | data: createActivity, | ||
118 | byActor, | ||
119 | toActors: parentsCommentActors, | ||
120 | transaction, | ||
121 | actorsException, | ||
122 | contextType: 'Comment' | ||
123 | }) | ||
105 | 124 | ||
106 | // Broadcast to our followers | 125 | // Broadcast to our followers |
107 | await broadcastToFollowers(createActivity, byActor, [ byActor ], t) | 126 | await broadcastToFollowers({ |
127 | data: createActivity, | ||
128 | byActor, | ||
129 | toFollowersOf: [ byActor ], | ||
130 | transaction, | ||
131 | contextType: 'Comment' | ||
132 | }) | ||
108 | 133 | ||
109 | // Send to actors involved in the comment | 134 | // Send to actors involved in the comment |
110 | if (isOrigin) return broadcastToFollowers(createActivity, byActor, actorsInvolvedInComment, t, actorsException) | 135 | if (isOrigin) { |
136 | return broadcastToFollowers({ | ||
137 | data: createActivity, | ||
138 | byActor, | ||
139 | toFollowersOf: actorsInvolvedInComment, | ||
140 | transaction, | ||
141 | actorsException, | ||
142 | contextType: 'Comment' | ||
143 | }) | ||
144 | } | ||
111 | 145 | ||
112 | // Send to origin | 146 | // Send to origin |
113 | t.afterCommit(() => unicastTo(createActivity, byActor, comment.Video.VideoChannel.Account.Actor.getSharedInbox())) | 147 | return transaction.afterCommit(() => { |
148 | return unicastTo({ | ||
149 | data: createActivity, | ||
150 | byActor, | ||
151 | toActorUrl: comment.Video.VideoChannel.Account.Actor.getSharedInbox(), | ||
152 | contextType: 'Comment' | ||
153 | }) | ||
154 | }) | ||
114 | } | 155 | } |
115 | 156 | ||
116 | function buildCreateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityCreate { | 157 | function buildCreateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityCreate { |
@@ -144,8 +185,8 @@ async function sendVideoRelatedCreateActivity (options: { | |||
144 | video: MVideoAccountLight | 185 | video: MVideoAccountLight |
145 | url: string | 186 | url: string |
146 | object: any | 187 | object: any |
188 | contextType: ContextType | ||
147 | transaction?: Transaction | 189 | transaction?: Transaction |
148 | contextType?: ContextType | ||
149 | }) { | 190 | }) { |
150 | const activityBuilder = (audience: ActivityAudience) => { | 191 | const activityBuilder = (audience: ActivityAudience) => { |
151 | return buildCreateActivity(options.url, options.byActor, options.object, audience) | 192 | return buildCreateActivity(options.url, options.byActor, options.object, audience) |
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts index 39216cdeb..0d85d9001 100644 --- a/server/lib/activitypub/send/send-delete.ts +++ b/server/lib/activitypub/send/send-delete.ts | |||
@@ -23,16 +23,16 @@ async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transact | |||
23 | return buildDeleteActivity(url, video.url, byActor, audience) | 23 | return buildDeleteActivity(url, video.url, byActor, audience) |
24 | } | 24 | } |
25 | 25 | ||
26 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction }) | 26 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, contextType: 'Delete', transaction }) |
27 | } | 27 | } |
28 | 28 | ||
29 | async function sendDeleteActor (byActor: ActorModel, t: Transaction) { | 29 | async function sendDeleteActor (byActor: ActorModel, transaction: Transaction) { |
30 | logger.info('Creating job to broadcast delete of actor %s.', byActor.url) | 30 | logger.info('Creating job to broadcast delete of actor %s.', byActor.url) |
31 | 31 | ||
32 | const url = getDeleteActivityPubUrl(byActor.url) | 32 | const url = getDeleteActivityPubUrl(byActor.url) |
33 | const activity = buildDeleteActivity(url, byActor.url, byActor) | 33 | const activity = buildDeleteActivity(url, byActor.url, byActor) |
34 | 34 | ||
35 | const actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t) | 35 | const actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, transaction) |
36 | 36 | ||
37 | // In case the actor did not have any videos | 37 | // In case the actor did not have any videos |
38 | const serverActor = await getServerActor() | 38 | const serverActor = await getServerActor() |
@@ -40,10 +40,16 @@ async function sendDeleteActor (byActor: ActorModel, t: Transaction) { | |||
40 | 40 | ||
41 | actorsInvolved.push(byActor) | 41 | actorsInvolved.push(byActor) |
42 | 42 | ||
43 | return broadcastToFollowers(activity, byActor, actorsInvolved, t) | 43 | return broadcastToFollowers({ |
44 | data: activity, | ||
45 | byActor, | ||
46 | toFollowersOf: actorsInvolved, | ||
47 | contextType: 'Delete', | ||
48 | transaction | ||
49 | }) | ||
44 | } | 50 | } |
45 | 51 | ||
46 | async function sendDeleteVideoComment (videoComment: MCommentOwnerVideo, t: Transaction) { | 52 | async function sendDeleteVideoComment (videoComment: MCommentOwnerVideo, transaction: Transaction) { |
47 | logger.info('Creating job to send delete of comment %s.', videoComment.url) | 53 | logger.info('Creating job to send delete of comment %s.', videoComment.url) |
48 | 54 | ||
49 | const isVideoOrigin = videoComment.Video.isOwned() | 55 | const isVideoOrigin = videoComment.Video.isOwned() |
@@ -53,10 +59,10 @@ async function sendDeleteVideoComment (videoComment: MCommentOwnerVideo, t: Tran | |||
53 | ? videoComment.Account.Actor | 59 | ? videoComment.Account.Actor |
54 | : videoComment.Video.VideoChannel.Account.Actor | 60 | : videoComment.Video.VideoChannel.Account.Actor |
55 | 61 | ||
56 | const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, t) | 62 | const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, transaction) |
57 | const threadParentCommentsFiltered = threadParentComments.filter(c => !c.isDeleted()) | 63 | const threadParentCommentsFiltered = threadParentComments.filter(c => !c.isDeleted()) |
58 | 64 | ||
59 | const actorsInvolvedInComment = await getActorsInvolvedInVideo(videoComment.Video, t) | 65 | const actorsInvolvedInComment = await getActorsInvolvedInVideo(videoComment.Video, transaction) |
60 | actorsInvolvedInComment.push(byActor) // Add the actor that commented the video | 66 | actorsInvolvedInComment.push(byActor) // Add the actor that commented the video |
61 | 67 | ||
62 | const audience = getVideoCommentAudience(videoComment, threadParentCommentsFiltered, actorsInvolvedInComment, isVideoOrigin) | 68 | const audience = getVideoCommentAudience(videoComment, threadParentCommentsFiltered, actorsInvolvedInComment, isVideoOrigin) |
@@ -64,19 +70,48 @@ async function sendDeleteVideoComment (videoComment: MCommentOwnerVideo, t: Tran | |||
64 | 70 | ||
65 | // This was a reply, send it to the parent actors | 71 | // This was a reply, send it to the parent actors |
66 | const actorsException = [ byActor ] | 72 | const actorsException = [ byActor ] |
67 | await broadcastToActors(activity, byActor, threadParentCommentsFiltered.map(c => c.Account.Actor), t, actorsException) | 73 | await broadcastToActors({ |
74 | data: activity, | ||
75 | byActor, | ||
76 | toActors: threadParentCommentsFiltered.map(c => c.Account.Actor), | ||
77 | transaction, | ||
78 | contextType: 'Delete', | ||
79 | actorsException | ||
80 | }) | ||
68 | 81 | ||
69 | // Broadcast to our followers | 82 | // Broadcast to our followers |
70 | await broadcastToFollowers(activity, byActor, [ byActor ], t) | 83 | await broadcastToFollowers({ |
84 | data: activity, | ||
85 | byActor, | ||
86 | toFollowersOf: [ byActor ], | ||
87 | contextType: 'Delete', | ||
88 | transaction | ||
89 | }) | ||
71 | 90 | ||
72 | // Send to actors involved in the comment | 91 | // Send to actors involved in the comment |
73 | if (isVideoOrigin) return broadcastToFollowers(activity, byActor, actorsInvolvedInComment, t, actorsException) | 92 | if (isVideoOrigin) { |
93 | return broadcastToFollowers({ | ||
94 | data: activity, | ||
95 | byActor, | ||
96 | toFollowersOf: actorsInvolvedInComment, | ||
97 | transaction, | ||
98 | contextType: 'Delete', | ||
99 | actorsException | ||
100 | }) | ||
101 | } | ||
74 | 102 | ||
75 | // Send to origin | 103 | // Send to origin |
76 | t.afterCommit(() => unicastTo(activity, byActor, videoComment.Video.VideoChannel.Account.Actor.getSharedInbox())) | 104 | return transaction.afterCommit(() => { |
105 | return unicastTo({ | ||
106 | data: activity, | ||
107 | byActor, | ||
108 | toActorUrl: videoComment.Video.VideoChannel.Account.Actor.getSharedInbox(), | ||
109 | contextType: 'Delete' | ||
110 | }) | ||
111 | }) | ||
77 | } | 112 | } |
78 | 113 | ||
79 | async function sendDeleteVideoPlaylist (videoPlaylist: MVideoPlaylistFullSummary, t: Transaction) { | 114 | async function sendDeleteVideoPlaylist (videoPlaylist: MVideoPlaylistFullSummary, transaction: Transaction) { |
80 | logger.info('Creating job to send delete of playlist %s.', videoPlaylist.url) | 115 | logger.info('Creating job to send delete of playlist %s.', videoPlaylist.url) |
81 | 116 | ||
82 | const byActor = videoPlaylist.OwnerAccount.Actor | 117 | const byActor = videoPlaylist.OwnerAccount.Actor |
@@ -89,7 +124,13 @@ async function sendDeleteVideoPlaylist (videoPlaylist: MVideoPlaylistFullSummary | |||
89 | 124 | ||
90 | if (videoPlaylist.VideoChannel) toFollowersOf.push(videoPlaylist.VideoChannel.Actor) | 125 | if (videoPlaylist.VideoChannel) toFollowersOf.push(videoPlaylist.VideoChannel.Actor) |
91 | 126 | ||
92 | return broadcastToFollowers(activity, byActor, toFollowersOf, t) | 127 | return broadcastToFollowers({ |
128 | data: activity, | ||
129 | byActor, | ||
130 | toFollowersOf, | ||
131 | contextType: 'Delete', | ||
132 | transaction | ||
133 | }) | ||
93 | } | 134 | } |
94 | 135 | ||
95 | // --------------------------------------------------------------------------- | 136 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/activitypub/send/send-dislike.ts b/server/lib/activitypub/send/send-dislike.ts index ecb11e9bf..959e74823 100644 --- a/server/lib/activitypub/send/send-dislike.ts +++ b/server/lib/activitypub/send/send-dislike.ts | |||
@@ -6,7 +6,7 @@ import { audiencify, getAudience } from '../audience' | |||
6 | import { getVideoDislikeActivityPubUrlByLocalActor } from '../url' | 6 | import { getVideoDislikeActivityPubUrlByLocalActor } from '../url' |
7 | import { sendVideoActivityToOrigin } from './shared/send-utils' | 7 | import { sendVideoActivityToOrigin } from './shared/send-utils' |
8 | 8 | ||
9 | function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { | 9 | function sendDislike (byActor: MActor, video: MVideoAccountLight, transaction: Transaction) { |
10 | logger.info('Creating job to dislike %s.', video.url) | 10 | logger.info('Creating job to dislike %s.', video.url) |
11 | 11 | ||
12 | const activityBuilder = (audience: ActivityAudience) => { | 12 | const activityBuilder = (audience: ActivityAudience) => { |
@@ -15,7 +15,7 @@ function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction | |||
15 | return buildDislikeActivity(url, byActor, video, audience) | 15 | return buildDislikeActivity(url, byActor, video, audience) |
16 | } | 16 | } |
17 | 17 | ||
18 | return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction: t }) | 18 | return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction, contextType: 'Rate' }) |
19 | } | 19 | } |
20 | 20 | ||
21 | function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike { | 21 | function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike { |
diff --git a/server/lib/activitypub/send/send-flag.ts b/server/lib/activitypub/send/send-flag.ts index 6df4e7eb8..138eb5adc 100644 --- a/server/lib/activitypub/send/send-flag.ts +++ b/server/lib/activitypub/send/send-flag.ts | |||
@@ -17,16 +17,20 @@ function sendAbuse (byActor: MActor, abuse: MAbuseAP, flaggedAccount: MAccountLi | |||
17 | const audience = { to: [ flaggedAccount.Actor.url ], cc: [] } | 17 | const audience = { to: [ flaggedAccount.Actor.url ], cc: [] } |
18 | const flagActivity = buildFlagActivity(url, byActor, abuse, audience) | 18 | const flagActivity = buildFlagActivity(url, byActor, abuse, audience) |
19 | 19 | ||
20 | t.afterCommit(() => unicastTo(flagActivity, byActor, flaggedAccount.Actor.getSharedInbox())) | 20 | return t.afterCommit(() => { |
21 | return unicastTo({ | ||
22 | data: flagActivity, | ||
23 | byActor, | ||
24 | toActorUrl: flaggedAccount.Actor.getSharedInbox(), | ||
25 | contextType: 'Flag' | ||
26 | }) | ||
27 | }) | ||
21 | } | 28 | } |
22 | 29 | ||
23 | function buildFlagActivity (url: string, byActor: MActor, abuse: MAbuseAP, audience: ActivityAudience): ActivityFlag { | 30 | function buildFlagActivity (url: string, byActor: MActor, abuse: MAbuseAP, audience: ActivityAudience): ActivityFlag { |
24 | if (!audience) audience = getAudience(byActor) | 31 | if (!audience) audience = getAudience(byActor) |
25 | 32 | ||
26 | const activity = Object.assign( | 33 | const activity = { id: url, actor: byActor.url, ...abuse.toActivityPubObject() } |
27 | { id: url, actor: byActor.url }, | ||
28 | abuse.toActivityPubObject() | ||
29 | ) | ||
30 | 34 | ||
31 | return audiencify(activity, audience) | 35 | return audiencify(activity, audience) |
32 | } | 36 | } |
diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts index aeeb50a2a..57501dadb 100644 --- a/server/lib/activitypub/send/send-follow.ts +++ b/server/lib/activitypub/send/send-follow.ts | |||
@@ -15,7 +15,9 @@ function sendFollow (actorFollow: MActorFollowActors, t: Transaction) { | |||
15 | 15 | ||
16 | const data = buildFollowActivity(actorFollow.url, me, following) | 16 | const data = buildFollowActivity(actorFollow.url, me, following) |
17 | 17 | ||
18 | t.afterCommit(() => unicastTo(data, me, following.inboxUrl)) | 18 | return t.afterCommit(() => { |
19 | return unicastTo({ data, byActor: me, toActorUrl: following.inboxUrl, contextType: 'Follow' }) | ||
20 | }) | ||
19 | } | 21 | } |
20 | 22 | ||
21 | function buildFollowActivity (url: string, byActor: MActor, targetActor: MActor): ActivityFollow { | 23 | function buildFollowActivity (url: string, byActor: MActor, targetActor: MActor): ActivityFollow { |
diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts index a5fe95e0a..46c9fdec9 100644 --- a/server/lib/activitypub/send/send-like.ts +++ b/server/lib/activitypub/send/send-like.ts | |||
@@ -6,7 +6,7 @@ import { audiencify, getAudience } from '../audience' | |||
6 | import { getVideoLikeActivityPubUrlByLocalActor } from '../url' | 6 | import { getVideoLikeActivityPubUrlByLocalActor } from '../url' |
7 | import { sendVideoActivityToOrigin } from './shared/send-utils' | 7 | import { sendVideoActivityToOrigin } from './shared/send-utils' |
8 | 8 | ||
9 | function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { | 9 | function sendLike (byActor: MActor, video: MVideoAccountLight, transaction: Transaction) { |
10 | logger.info('Creating job to like %s.', video.url) | 10 | logger.info('Creating job to like %s.', video.url) |
11 | 11 | ||
12 | const activityBuilder = (audience: ActivityAudience) => { | 12 | const activityBuilder = (audience: ActivityAudience) => { |
@@ -15,7 +15,7 @@ function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { | |||
15 | return buildLikeActivity(url, byActor, video, audience) | 15 | return buildLikeActivity(url, byActor, video, audience) |
16 | } | 16 | } |
17 | 17 | ||
18 | return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction: t }) | 18 | return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction, contextType: 'Rate' }) |
19 | } | 19 | } |
20 | 20 | ||
21 | function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike { | 21 | function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike { |
diff --git a/server/lib/activitypub/send/send-reject.ts b/server/lib/activitypub/send/send-reject.ts index 01b8f743b..83d8dfba7 100644 --- a/server/lib/activitypub/send/send-reject.ts +++ b/server/lib/activitypub/send/send-reject.ts | |||
@@ -18,7 +18,7 @@ function sendReject (followUrl: string, follower: MActor, following: MActor) { | |||
18 | const url = getLocalActorFollowRejectActivityPubUrl(follower, following) | 18 | const url = getLocalActorFollowRejectActivityPubUrl(follower, following) |
19 | const data = buildRejectActivity(url, following, followData) | 19 | const data = buildRejectActivity(url, following, followData) |
20 | 20 | ||
21 | return unicastTo(data, following, follower.inboxUrl) | 21 | return unicastTo({ data, byActor: following, toActorUrl: follower.inboxUrl, contextType: 'Reject' }) |
22 | } | 22 | } |
23 | 23 | ||
24 | // --------------------------------------------------------------------------- | 24 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index 948ca0d7a..36d7ef991 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts | |||
@@ -6,7 +6,8 @@ import { | |||
6 | ActivityDislike, | 6 | ActivityDislike, |
7 | ActivityFollow, | 7 | ActivityFollow, |
8 | ActivityLike, | 8 | ActivityLike, |
9 | ActivityUndo | 9 | ActivityUndo, |
10 | ContextType | ||
10 | } from '@shared/models' | 11 | } from '@shared/models' |
11 | import { logger } from '../../../helpers/logger' | 12 | import { logger } from '../../../helpers/logger' |
12 | import { VideoModel } from '../../../models/video/video' | 13 | import { VideoModel } from '../../../models/video/video' |
@@ -43,24 +44,37 @@ function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) { | |||
43 | const followActivity = buildFollowActivity(actorFollow.url, me, following) | 44 | const followActivity = buildFollowActivity(actorFollow.url, me, following) |
44 | const undoActivity = undoActivityData(undoUrl, me, followActivity) | 45 | const undoActivity = undoActivityData(undoUrl, me, followActivity) |
45 | 46 | ||
46 | t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl)) | 47 | return t.afterCommit(() => { |
48 | return unicastTo({ | ||
49 | data: undoActivity, | ||
50 | byActor: me, | ||
51 | toActorUrl: following.inboxUrl, | ||
52 | contextType: 'Follow' | ||
53 | }) | ||
54 | }) | ||
47 | } | 55 | } |
48 | 56 | ||
49 | // --------------------------------------------------------------------------- | 57 | // --------------------------------------------------------------------------- |
50 | 58 | ||
51 | async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) { | 59 | async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, transaction: Transaction) { |
52 | logger.info('Creating job to undo announce %s.', videoShare.url) | 60 | logger.info('Creating job to undo announce %s.', videoShare.url) |
53 | 61 | ||
54 | const undoUrl = getUndoActivityPubUrl(videoShare.url) | 62 | const undoUrl = getUndoActivityPubUrl(videoShare.url) |
55 | 63 | ||
56 | const { activity: announceActivity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t) | 64 | const { activity: announce, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, transaction) |
57 | const undoActivity = undoActivityData(undoUrl, byActor, announceActivity) | 65 | const undoActivity = undoActivityData(undoUrl, byActor, announce) |
58 | 66 | ||
59 | const followersException = [ byActor ] | 67 | return broadcastToFollowers({ |
60 | return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException) | 68 | data: undoActivity, |
69 | byActor, | ||
70 | toFollowersOf: actorsInvolvedInVideo, | ||
71 | transaction, | ||
72 | actorsException: [ byActor ], | ||
73 | contextType: 'Announce' | ||
74 | }) | ||
61 | } | 75 | } |
62 | 76 | ||
63 | async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) { | 77 | async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, transaction: Transaction) { |
64 | logger.info('Creating job to undo cache file %s.', redundancyModel.url) | 78 | logger.info('Creating job to undo cache file %s.', redundancyModel.url) |
65 | 79 | ||
66 | const associatedVideo = redundancyModel.getVideo() | 80 | const associatedVideo = redundancyModel.getVideo() |
@@ -72,7 +86,14 @@ async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedund | |||
72 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(associatedVideo.id) | 86 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(associatedVideo.id) |
73 | const createActivity = buildCreateActivity(redundancyModel.url, byActor, redundancyModel.toActivityPubObject()) | 87 | const createActivity = buildCreateActivity(redundancyModel.url, byActor, redundancyModel.toActivityPubObject()) |
74 | 88 | ||
75 | return sendUndoVideoRelatedActivity({ byActor, video, url: redundancyModel.url, activity: createActivity, transaction: t }) | 89 | return sendUndoVideoRelatedActivity({ |
90 | byActor, | ||
91 | video, | ||
92 | url: redundancyModel.url, | ||
93 | activity: createActivity, | ||
94 | contextType: 'CacheFile', | ||
95 | transaction | ||
96 | }) | ||
76 | } | 97 | } |
77 | 98 | ||
78 | // --------------------------------------------------------------------------- | 99 | // --------------------------------------------------------------------------- |
@@ -83,7 +104,7 @@ async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Tran | |||
83 | const likeUrl = getVideoLikeActivityPubUrlByLocalActor(byActor, video) | 104 | const likeUrl = getVideoLikeActivityPubUrlByLocalActor(byActor, video) |
84 | const likeActivity = buildLikeActivity(likeUrl, byActor, video) | 105 | const likeActivity = buildLikeActivity(likeUrl, byActor, video) |
85 | 106 | ||
86 | return sendUndoVideoToOriginActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t }) | 107 | return sendUndoVideoRateToOriginActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t }) |
87 | } | 108 | } |
88 | 109 | ||
89 | async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { | 110 | async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
@@ -92,7 +113,7 @@ async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: T | |||
92 | const dislikeUrl = getVideoDislikeActivityPubUrlByLocalActor(byActor, video) | 113 | const dislikeUrl = getVideoDislikeActivityPubUrlByLocalActor(byActor, video) |
93 | const dislikeActivity = buildDislikeActivity(dislikeUrl, byActor, video) | 114 | const dislikeActivity = buildDislikeActivity(dislikeUrl, byActor, video) |
94 | 115 | ||
95 | return sendUndoVideoToOriginActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t }) | 116 | return sendUndoVideoRateToOriginActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t }) |
96 | } | 117 | } |
97 | 118 | ||
98 | // --------------------------------------------------------------------------- | 119 | // --------------------------------------------------------------------------- |
@@ -131,6 +152,7 @@ async function sendUndoVideoRelatedActivity (options: { | |||
131 | video: MVideoAccountLight | 152 | video: MVideoAccountLight |
132 | url: string | 153 | url: string |
133 | activity: ActivityFollow | ActivityCreate | ActivityAnnounce | 154 | activity: ActivityFollow | ActivityCreate | ActivityAnnounce |
155 | contextType: ContextType | ||
134 | transaction: Transaction | 156 | transaction: Transaction |
135 | }) { | 157 | }) { |
136 | const activityBuilder = (audience: ActivityAudience) => { | 158 | const activityBuilder = (audience: ActivityAudience) => { |
@@ -142,7 +164,7 @@ async function sendUndoVideoRelatedActivity (options: { | |||
142 | return sendVideoRelatedActivity(activityBuilder, options) | 164 | return sendVideoRelatedActivity(activityBuilder, options) |
143 | } | 165 | } |
144 | 166 | ||
145 | async function sendUndoVideoToOriginActivity (options: { | 167 | async function sendUndoVideoRateToOriginActivity (options: { |
146 | byActor: MActor | 168 | byActor: MActor |
147 | video: MVideoAccountLight | 169 | video: MVideoAccountLight |
148 | url: string | 170 | url: string |
@@ -155,5 +177,5 @@ async function sendUndoVideoToOriginActivity (options: { | |||
155 | return undoActivityData(undoUrl, options.byActor, options.activity, audience) | 177 | return undoActivityData(undoUrl, options.byActor, options.activity, audience) |
156 | } | 178 | } |
157 | 179 | ||
158 | return sendVideoActivityToOrigin(activityBuilder, options) | 180 | return sendVideoActivityToOrigin(activityBuilder, { ...options, contextType: 'Rate' }) |
159 | } | 181 | } |
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts index 7c9e72cbc..3577ece02 100644 --- a/server/lib/activitypub/send/send-update.ts +++ b/server/lib/activitypub/send/send-update.ts | |||
@@ -20,20 +20,20 @@ import { getUpdateActivityPubUrl } from '../url' | |||
20 | import { getActorsInvolvedInVideo } from './shared' | 20 | import { getActorsInvolvedInVideo } from './shared' |
21 | import { broadcastToFollowers, sendVideoRelatedActivity } from './shared/send-utils' | 21 | import { broadcastToFollowers, sendVideoRelatedActivity } from './shared/send-utils' |
22 | 22 | ||
23 | async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) { | 23 | async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, transaction: Transaction, overriddenByActor?: MActor) { |
24 | const video = videoArg as MVideoAP | 24 | const video = videoArg as MVideoAP |
25 | 25 | ||
26 | if (!video.hasPrivacyForFederation()) return undefined | 26 | if (!video.hasPrivacyForFederation()) return undefined |
27 | 27 | ||
28 | logger.info('Creating job to update video %s.', video.url) | 28 | logger.info('Creating job to update video %s.', video.url) |
29 | 29 | ||
30 | const byActor = overrodeByActor || video.VideoChannel.Account.Actor | 30 | const byActor = overriddenByActor || video.VideoChannel.Account.Actor |
31 | 31 | ||
32 | const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString()) | 32 | const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString()) |
33 | 33 | ||
34 | // Needed to build the AP object | 34 | // Needed to build the AP object |
35 | if (!video.VideoCaptions) { | 35 | if (!video.VideoCaptions) { |
36 | video.VideoCaptions = await video.$get('VideoCaptions', { transaction: t }) | 36 | video.VideoCaptions = await video.$get('VideoCaptions', { transaction }) |
37 | } | 37 | } |
38 | 38 | ||
39 | const videoObject = video.toActivityPubObject() | 39 | const videoObject = video.toActivityPubObject() |
@@ -41,13 +41,19 @@ async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction | |||
41 | 41 | ||
42 | const updateActivity = buildUpdateActivity(url, byActor, videoObject, audience) | 42 | const updateActivity = buildUpdateActivity(url, byActor, videoObject, audience) |
43 | 43 | ||
44 | const actorsInvolved = await getActorsInvolvedInVideo(video, t) | 44 | const actorsInvolved = await getActorsInvolvedInVideo(video, transaction) |
45 | if (overrodeByActor) actorsInvolved.push(overrodeByActor) | 45 | if (overriddenByActor) actorsInvolved.push(overriddenByActor) |
46 | 46 | ||
47 | return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) | 47 | return broadcastToFollowers({ |
48 | data: updateActivity, | ||
49 | byActor, | ||
50 | toFollowersOf: actorsInvolved, | ||
51 | contextType: 'Video', | ||
52 | transaction | ||
53 | }) | ||
48 | } | 54 | } |
49 | 55 | ||
50 | async function sendUpdateActor (accountOrChannel: MChannelDefault | MAccountDefault, t: Transaction) { | 56 | async function sendUpdateActor (accountOrChannel: MChannelDefault | MAccountDefault, transaction: Transaction) { |
51 | const byActor = accountOrChannel.Actor | 57 | const byActor = accountOrChannel.Actor |
52 | 58 | ||
53 | logger.info('Creating job to update actor %s.', byActor.url) | 59 | logger.info('Creating job to update actor %s.', byActor.url) |
@@ -60,15 +66,21 @@ async function sendUpdateActor (accountOrChannel: MChannelDefault | MAccountDefa | |||
60 | let actorsInvolved: MActor[] | 66 | let actorsInvolved: MActor[] |
61 | if (accountOrChannel instanceof AccountModel) { | 67 | if (accountOrChannel instanceof AccountModel) { |
62 | // Actors that shared my videos are involved too | 68 | // Actors that shared my videos are involved too |
63 | actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t) | 69 | actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, transaction) |
64 | } else { | 70 | } else { |
65 | // Actors that shared videos of my channel are involved too | 71 | // Actors that shared videos of my channel are involved too |
66 | actorsInvolved = await VideoShareModel.loadActorsByVideoChannel(accountOrChannel.id, t) | 72 | actorsInvolved = await VideoShareModel.loadActorsByVideoChannel(accountOrChannel.id, transaction) |
67 | } | 73 | } |
68 | 74 | ||
69 | actorsInvolved.push(byActor) | 75 | actorsInvolved.push(byActor) |
70 | 76 | ||
71 | return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) | 77 | return broadcastToFollowers({ |
78 | data: updateActivity, | ||
79 | byActor, | ||
80 | toFollowersOf: actorsInvolved, | ||
81 | transaction, | ||
82 | contextType: 'Actor' | ||
83 | }) | ||
72 | } | 84 | } |
73 | 85 | ||
74 | async function sendUpdateCacheFile (byActor: MActorLight, redundancyModel: MVideoRedundancyVideo) { | 86 | async function sendUpdateCacheFile (byActor: MActorLight, redundancyModel: MVideoRedundancyVideo) { |
@@ -92,7 +104,7 @@ async function sendUpdateCacheFile (byActor: MActorLight, redundancyModel: MVide | |||
92 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, contextType: 'CacheFile' }) | 104 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, contextType: 'CacheFile' }) |
93 | } | 105 | } |
94 | 106 | ||
95 | async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, t: Transaction) { | 107 | async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, transaction: Transaction) { |
96 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined | 108 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined |
97 | 109 | ||
98 | const byActor = videoPlaylist.OwnerAccount.Actor | 110 | const byActor = videoPlaylist.OwnerAccount.Actor |
@@ -101,7 +113,7 @@ async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, t: Tr | |||
101 | 113 | ||
102 | const url = getUpdateActivityPubUrl(videoPlaylist.url, videoPlaylist.updatedAt.toISOString()) | 114 | const url = getUpdateActivityPubUrl(videoPlaylist.url, videoPlaylist.updatedAt.toISOString()) |
103 | 115 | ||
104 | const object = await videoPlaylist.toActivityPubObject(null, t) | 116 | const object = await videoPlaylist.toActivityPubObject(null, transaction) |
105 | const audience = getAudience(byActor, videoPlaylist.privacy === VideoPlaylistPrivacy.PUBLIC) | 117 | const audience = getAudience(byActor, videoPlaylist.privacy === VideoPlaylistPrivacy.PUBLIC) |
106 | 118 | ||
107 | const updateActivity = buildUpdateActivity(url, byActor, object, audience) | 119 | const updateActivity = buildUpdateActivity(url, byActor, object, audience) |
@@ -111,7 +123,13 @@ async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, t: Tr | |||
111 | 123 | ||
112 | if (videoPlaylist.VideoChannel) toFollowersOf.push(videoPlaylist.VideoChannel.Actor) | 124 | if (videoPlaylist.VideoChannel) toFollowersOf.push(videoPlaylist.VideoChannel.Actor) |
113 | 125 | ||
114 | return broadcastToFollowers(updateActivity, byActor, toFollowersOf, t) | 126 | return broadcastToFollowers({ |
127 | data: updateActivity, | ||
128 | byActor, | ||
129 | toFollowersOf, | ||
130 | transaction, | ||
131 | contextType: 'Playlist' | ||
132 | }) | ||
115 | } | 133 | } |
116 | 134 | ||
117 | // --------------------------------------------------------------------------- | 135 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/activitypub/send/shared/send-utils.ts b/server/lib/activitypub/send/shared/send-utils.ts index 9e8f12fa8..dbcde91ee 100644 --- a/server/lib/activitypub/send/shared/send-utils.ts +++ b/server/lib/activitypub/send/shared/send-utils.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActorFollowHealthCache } from '@server/lib/actor-follow-health-cache' | 2 | import { ActorFollowHealthCache } from '@server/lib/actor-follow-health-cache' |
3 | import { getServerActor } from '@server/models/application/application' | 3 | import { getServerActor } from '@server/models/application/application' |
4 | import { Activity, ActivityAudience } from '@shared/models' | 4 | import { Activity, ActivityAudience, ActivitypubHttpBroadcastPayload } from '@shared/models' |
5 | import { ContextType } from '@shared/models/activitypub/context' | 5 | import { ContextType } from '@shared/models/activitypub/context' |
6 | import { afterCommitIfTransaction } from '../../../../helpers/database-utils' | 6 | import { afterCommitIfTransaction } from '../../../../helpers/database-utils' |
7 | import { logger } from '../../../../helpers/logger' | 7 | import { logger } from '../../../../helpers/logger' |
@@ -14,8 +14,8 @@ import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getOriginVideoAud | |||
14 | async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { | 14 | async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { |
15 | byActor: MActorLight | 15 | byActor: MActorLight |
16 | video: MVideoImmutable | MVideoAccountLight | 16 | video: MVideoImmutable | MVideoAccountLight |
17 | contextType: ContextType | ||
17 | transaction?: Transaction | 18 | transaction?: Transaction |
18 | contextType?: ContextType | ||
19 | }) { | 19 | }) { |
20 | const { byActor, video, transaction, contextType } = options | 20 | const { byActor, video, transaction, contextType } = options |
21 | 21 | ||
@@ -32,15 +32,23 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud | |||
32 | 32 | ||
33 | const actorsException = [ byActor ] | 33 | const actorsException = [ byActor ] |
34 | 34 | ||
35 | return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, transaction, actorsException, contextType) | 35 | return broadcastToFollowers({ |
36 | data: activity, | ||
37 | byActor, | ||
38 | toFollowersOf: actorsInvolvedInVideo, | ||
39 | transaction, | ||
40 | actorsException, | ||
41 | contextType | ||
42 | }) | ||
36 | } | 43 | } |
37 | 44 | ||
38 | async function sendVideoActivityToOrigin (activityBuilder: (audience: ActivityAudience) => Activity, options: { | 45 | async function sendVideoActivityToOrigin (activityBuilder: (audience: ActivityAudience) => Activity, options: { |
39 | byActor: MActorLight | 46 | byActor: MActorLight |
40 | video: MVideoImmutable | MVideoAccountLight | 47 | video: MVideoImmutable | MVideoAccountLight |
48 | contextType: ContextType | ||
49 | |||
41 | actorsInvolvedInVideo?: MActorLight[] | 50 | actorsInvolvedInVideo?: MActorLight[] |
42 | transaction?: Transaction | 51 | transaction?: Transaction |
43 | contextType?: ContextType | ||
44 | }) { | 52 | }) { |
45 | const { byActor, video, actorsInvolvedInVideo, transaction, contextType } = options | 53 | const { byActor, video, actorsInvolvedInVideo, transaction, contextType } = options |
46 | 54 | ||
@@ -53,7 +61,12 @@ async function sendVideoActivityToOrigin (activityBuilder: (audience: ActivityAu | |||
53 | const activity = activityBuilder(audience) | 61 | const activity = activityBuilder(audience) |
54 | 62 | ||
55 | return afterCommitIfTransaction(transaction, () => { | 63 | return afterCommitIfTransaction(transaction, () => { |
56 | return unicastTo(activity, byActor, accountActor.getSharedInbox(), contextType) | 64 | return unicastTo({ |
65 | data: activity, | ||
66 | byActor, | ||
67 | toActorUrl: accountActor.getSharedInbox(), | ||
68 | contextType | ||
69 | }) | ||
57 | }) | 70 | }) |
58 | } | 71 | } |
59 | 72 | ||
@@ -100,41 +113,69 @@ async function forwardActivity ( | |||
100 | 113 | ||
101 | logger.debug('Creating forwarding job.', { uris }) | 114 | logger.debug('Creating forwarding job.', { uris }) |
102 | 115 | ||
103 | const payload = { | 116 | const payload: ActivitypubHttpBroadcastPayload = { |
104 | uris, | 117 | uris, |
105 | body: activity | 118 | body: activity, |
119 | contextType: null | ||
106 | } | 120 | } |
107 | return afterCommitIfTransaction(t, () => JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })) | 121 | return afterCommitIfTransaction(t, () => JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })) |
108 | } | 122 | } |
109 | 123 | ||
110 | // --------------------------------------------------------------------------- | 124 | // --------------------------------------------------------------------------- |
111 | 125 | ||
112 | async function broadcastToFollowers ( | 126 | async function broadcastToFollowers (options: { |
113 | data: any, | 127 | data: any |
114 | byActor: MActorId, | 128 | byActor: MActorId |
115 | toFollowersOf: MActorId[], | 129 | toFollowersOf: MActorId[] |
116 | t: Transaction, | 130 | transaction: Transaction |
117 | actorsException: MActorWithInboxes[] = [], | 131 | contextType: ContextType |
118 | contextType?: ContextType | 132 | |
119 | ) { | 133 | actorsException?: MActorWithInboxes[] |
120 | const uris = await computeFollowerUris(toFollowersOf, actorsException, t) | 134 | }) { |
135 | const { data, byActor, toFollowersOf, transaction, contextType, actorsException = [] } = options | ||
121 | 136 | ||
122 | return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor, contextType)) | 137 | const uris = await computeFollowerUris(toFollowersOf, actorsException, transaction) |
138 | |||
139 | return afterCommitIfTransaction(transaction, () => { | ||
140 | return broadcastTo({ | ||
141 | uris, | ||
142 | data, | ||
143 | byActor, | ||
144 | contextType | ||
145 | }) | ||
146 | }) | ||
123 | } | 147 | } |
124 | 148 | ||
125 | async function broadcastToActors ( | 149 | async function broadcastToActors (options: { |
126 | data: any, | 150 | data: any |
127 | byActor: MActorId, | 151 | byActor: MActorId |
128 | toActors: MActor[], | 152 | toActors: MActor[] |
129 | t?: Transaction, | 153 | transaction: Transaction |
130 | actorsException: MActorWithInboxes[] = [], | 154 | contextType: ContextType |
131 | contextType?: ContextType | 155 | actorsException?: MActorWithInboxes[] |
132 | ) { | 156 | }) { |
157 | const { data, byActor, toActors, transaction, contextType, actorsException = [] } = options | ||
158 | |||
133 | const uris = await computeUris(toActors, actorsException) | 159 | const uris = await computeUris(toActors, actorsException) |
134 | return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor, contextType)) | 160 | |
161 | return afterCommitIfTransaction(transaction, () => { | ||
162 | return broadcastTo({ | ||
163 | uris, | ||
164 | data, | ||
165 | byActor, | ||
166 | contextType | ||
167 | }) | ||
168 | }) | ||
135 | } | 169 | } |
136 | 170 | ||
137 | function broadcastTo (uris: string[], data: any, byActor: MActorId, contextType?: ContextType) { | 171 | function broadcastTo (options: { |
172 | uris: string[] | ||
173 | data: any | ||
174 | byActor: MActorId | ||
175 | contextType: ContextType | ||
176 | }) { | ||
177 | const { uris, data, byActor, contextType } = options | ||
178 | |||
138 | if (uris.length === 0) return undefined | 179 | if (uris.length === 0) return undefined |
139 | 180 | ||
140 | const broadcastUris: string[] = [] | 181 | const broadcastUris: string[] = [] |
@@ -174,7 +215,14 @@ function broadcastTo (uris: string[], data: any, byActor: MActorId, contextType? | |||
174 | } | 215 | } |
175 | } | 216 | } |
176 | 217 | ||
177 | function unicastTo (data: any, byActor: MActorId, toActorUrl: string, contextType?: ContextType) { | 218 | function unicastTo (options: { |
219 | data: any | ||
220 | byActor: MActorId | ||
221 | toActorUrl: string | ||
222 | contextType: ContextType | ||
223 | }) { | ||
224 | const { data, byActor, toActorUrl, contextType } = options | ||
225 | |||
178 | logger.debug('Creating unicast job.', { uri: toActorUrl }) | 226 | logger.debug('Creating unicast job.', { uri: toActorUrl }) |
179 | 227 | ||
180 | const payload = { | 228 | const payload = { |
diff --git a/server/lib/job-queue/handlers/activitypub-http-broadcast.ts b/server/lib/job-queue/handlers/activitypub-http-broadcast.ts index fbf01d276..709e8501f 100644 --- a/server/lib/job-queue/handlers/activitypub-http-broadcast.ts +++ b/server/lib/job-queue/handlers/activitypub-http-broadcast.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import { map } from 'bluebird' | 1 | import { map } from 'bluebird' |
2 | import { Job } from 'bull' | 2 | import { Job } from 'bull' |
3 | import { buildGlobalHeaders, buildSignedRequestOptions, computeBody } from '@server/lib/activitypub/send' | ||
3 | import { ActorFollowHealthCache } from '@server/lib/actor-follow-health-cache' | 4 | import { ActorFollowHealthCache } from '@server/lib/actor-follow-health-cache' |
4 | import { ActivitypubHttpBroadcastPayload } from '@shared/models' | 5 | import { ActivitypubHttpBroadcastPayload } from '@shared/models' |
5 | import { logger } from '../../../helpers/logger' | 6 | import { logger } from '../../../helpers/logger' |
6 | import { doRequest } from '../../../helpers/requests' | 7 | import { doRequest } from '../../../helpers/requests' |
7 | import { BROADCAST_CONCURRENCY } from '../../../initializers/constants' | 8 | import { BROADCAST_CONCURRENCY } from '../../../initializers/constants' |
8 | import { buildGlobalHeaders, buildSignedRequestOptions, computeBody } from './utils/activitypub-http-utils' | ||
9 | 9 | ||
10 | async function processActivityPubHttpBroadcast (job: Job) { | 10 | async function processActivityPubHttpBroadcast (job: Job) { |
11 | logger.info('Processing ActivityPub broadcast in job %d.', job.id) | 11 | logger.info('Processing ActivityPub broadcast in job %d.', job.id) |
diff --git a/server/lib/job-queue/handlers/activitypub-http-unicast.ts b/server/lib/job-queue/handlers/activitypub-http-unicast.ts index 673583d2b..99bcd3e8d 100644 --- a/server/lib/job-queue/handlers/activitypub-http-unicast.ts +++ b/server/lib/job-queue/handlers/activitypub-http-unicast.ts | |||
@@ -1,9 +1,9 @@ | |||
1 | import { Job } from 'bull' | 1 | import { Job } from 'bull' |
2 | import { buildGlobalHeaders, buildSignedRequestOptions, computeBody } from '@server/lib/activitypub/send' | ||
2 | import { ActivitypubHttpUnicastPayload } from '@shared/models' | 3 | import { ActivitypubHttpUnicastPayload } from '@shared/models' |
3 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
4 | import { doRequest } from '../../../helpers/requests' | 5 | import { doRequest } from '../../../helpers/requests' |
5 | import { ActorFollowHealthCache } from '../../actor-follow-health-cache' | 6 | import { ActorFollowHealthCache } from '../../actor-follow-health-cache' |
6 | import { buildGlobalHeaders, buildSignedRequestOptions, computeBody } from './utils/activitypub-http-utils' | ||
7 | 7 | ||
8 | async function processActivityPubHttpUnicast (job: Job) { | 8 | async function processActivityPubHttpUnicast (job: Job) { |
9 | logger.info('Processing ActivityPub unicast in job %d.', job.id) | 9 | logger.info('Processing ActivityPub unicast in job %d.', job.id) |
diff --git a/server/models/actor/actor.ts b/server/models/actor/actor.ts index fad2070ea..93145b8ae 100644 --- a/server/models/actor/actor.ts +++ b/server/models/actor/actor.ts | |||
@@ -605,7 +605,7 @@ export class ActorModel extends Model<Partial<AttributesOnly<ActorModel>>> { | |||
605 | image | 605 | image |
606 | } | 606 | } |
607 | 607 | ||
608 | return activityPubContextify(json) | 608 | return activityPubContextify(json, 'Actor') |
609 | } | 609 | } |
610 | 610 | ||
611 | getFollowerSharedInboxUrls (t: Transaction) { | 611 | getFollowerSharedInboxUrls (t: Transaction) { |
diff --git a/server/tests/api/activitypub/helpers.ts b/server/tests/api/activitypub/helpers.ts index e516cf49e..bc1de35d1 100644 --- a/server/tests/api/activitypub/helpers.ts +++ b/server/tests/api/activitypub/helpers.ts | |||
@@ -3,10 +3,10 @@ | |||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import { expect } from 'chai' | 4 | import { expect } from 'chai' |
5 | import { cloneDeep } from 'lodash' | 5 | import { cloneDeep } from 'lodash' |
6 | import { signAndContextify } from '@server/lib/activitypub/send' | ||
6 | import { buildRequestStub } from '@server/tests/shared' | 7 | import { buildRequestStub } from '@server/tests/shared' |
7 | import { buildAbsoluteFixturePath } from '@shared/core-utils' | 8 | import { buildAbsoluteFixturePath } from '@shared/core-utils' |
8 | import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../../../helpers/peertube-crypto' | 9 | import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../../../helpers/peertube-crypto' |
9 | import { buildSignedActivity } from '../../../lib/activitypub/activity' | ||
10 | 10 | ||
11 | describe('Test activity pub helpers', function () { | 11 | describe('Test activity pub helpers', function () { |
12 | describe('When checking the Linked Signature', function () { | 12 | describe('When checking the Linked Signature', function () { |
@@ -46,7 +46,7 @@ describe('Test activity pub helpers', function () { | |||
46 | const body = require(buildAbsoluteFixturePath('./ap-json/peertube/announce-without-context.json')) | 46 | const body = require(buildAbsoluteFixturePath('./ap-json/peertube/announce-without-context.json')) |
47 | 47 | ||
48 | const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey } | 48 | const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey } |
49 | const signedBody = await buildSignedActivity(actorSignature as any, body) | 49 | const signedBody = await signAndContextify(actorSignature as any, body, 'Announce') |
50 | 50 | ||
51 | const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9002/accounts/peertube' } | 51 | const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9002/accounts/peertube' } |
52 | const result = await isJsonLDSignatureVerified(fromActor as any, signedBody) | 52 | const result = await isJsonLDSignatureVerified(fromActor as any, signedBody) |
@@ -59,7 +59,7 @@ describe('Test activity pub helpers', function () { | |||
59 | const body = require(buildAbsoluteFixturePath('./ap-json/peertube/announce-without-context.json')) | 59 | const body = require(buildAbsoluteFixturePath('./ap-json/peertube/announce-without-context.json')) |
60 | 60 | ||
61 | const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey } | 61 | const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey } |
62 | const signedBody = await buildSignedActivity(actorSignature as any, body) | 62 | const signedBody = await signAndContextify(actorSignature as any, body, 'Announce') |
63 | 63 | ||
64 | const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9002/accounts/peertube' } | 64 | const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9002/accounts/peertube' } |
65 | const result = await isJsonLDSignatureVerified(fromActor as any, signedBody) | 65 | const result = await isJsonLDSignatureVerified(fromActor as any, signedBody) |
diff --git a/server/tests/api/activitypub/security.ts b/server/tests/api/activitypub/security.ts index da9880d7d..a070517b8 100644 --- a/server/tests/api/activitypub/security.ts +++ b/server/tests/api/activitypub/security.ts | |||
@@ -4,9 +4,8 @@ import 'mocha' | |||
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import { buildDigest } from '@server/helpers/peertube-crypto' | 5 | import { buildDigest } from '@server/helpers/peertube-crypto' |
6 | import { HTTP_SIGNATURE } from '@server/initializers/constants' | 6 | import { HTTP_SIGNATURE } from '@server/initializers/constants' |
7 | import { buildSignedActivity } from '@server/lib/activitypub/activity' | ||
8 | import { activityPubContextify } from '@server/lib/activitypub/context' | 7 | import { activityPubContextify } from '@server/lib/activitypub/context' |
9 | import { buildGlobalHeaders } from '@server/lib/job-queue/handlers/utils/activitypub-http-utils' | 8 | import { buildGlobalHeaders, signAndContextify } from '@server/lib/activitypub/send' |
10 | import { makeFollowRequest, makePOSTAPRequest } from '@server/tests/shared' | 9 | import { makeFollowRequest, makePOSTAPRequest } from '@server/tests/shared' |
11 | import { buildAbsoluteFixturePath, wait } from '@shared/core-utils' | 10 | import { buildAbsoluteFixturePath, wait } from '@shared/core-utils' |
12 | import { HttpStatusCode } from '@shared/models' | 11 | import { HttpStatusCode } from '@shared/models' |
@@ -81,7 +80,7 @@ describe('Test ActivityPub security', function () { | |||
81 | describe('When checking HTTP signature', function () { | 80 | describe('When checking HTTP signature', function () { |
82 | 81 | ||
83 | it('Should fail with an invalid digest', async function () { | 82 | it('Should fail with an invalid digest', async function () { |
84 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) | 83 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') |
85 | const headers = { | 84 | const headers = { |
86 | Digest: buildDigest({ hello: 'coucou' }) | 85 | Digest: buildDigest({ hello: 'coucou' }) |
87 | } | 86 | } |
@@ -95,7 +94,7 @@ describe('Test ActivityPub security', function () { | |||
95 | }) | 94 | }) |
96 | 95 | ||
97 | it('Should fail with an invalid date', async function () { | 96 | it('Should fail with an invalid date', async function () { |
98 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) | 97 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') |
99 | const headers = buildGlobalHeaders(body) | 98 | const headers = buildGlobalHeaders(body) |
100 | headers['date'] = 'Wed, 21 Oct 2015 07:28:00 GMT' | 99 | headers['date'] = 'Wed, 21 Oct 2015 07:28:00 GMT' |
101 | 100 | ||
@@ -111,7 +110,7 @@ describe('Test ActivityPub security', function () { | |||
111 | await setKeysOfServer(servers[0], servers[1], invalidKeys.publicKey, invalidKeys.privateKey) | 110 | await setKeysOfServer(servers[0], servers[1], invalidKeys.publicKey, invalidKeys.privateKey) |
112 | await setKeysOfServer(servers[1], servers[1], invalidKeys.publicKey, invalidKeys.privateKey) | 111 | await setKeysOfServer(servers[1], servers[1], invalidKeys.publicKey, invalidKeys.privateKey) |
113 | 112 | ||
114 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) | 113 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') |
115 | const headers = buildGlobalHeaders(body) | 114 | const headers = buildGlobalHeaders(body) |
116 | 115 | ||
117 | try { | 116 | try { |
@@ -126,7 +125,7 @@ describe('Test ActivityPub security', function () { | |||
126 | await setKeysOfServer(servers[0], servers[1], keys.publicKey, keys.privateKey) | 125 | await setKeysOfServer(servers[0], servers[1], keys.publicKey, keys.privateKey) |
127 | await setKeysOfServer(servers[1], servers[1], keys.publicKey, keys.privateKey) | 126 | await setKeysOfServer(servers[1], servers[1], keys.publicKey, keys.privateKey) |
128 | 127 | ||
129 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) | 128 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') |
130 | const headers = buildGlobalHeaders(body) | 129 | const headers = buildGlobalHeaders(body) |
131 | 130 | ||
132 | const signatureOptions = baseHttpSignature() | 131 | const signatureOptions = baseHttpSignature() |
@@ -149,7 +148,7 @@ describe('Test ActivityPub security', function () { | |||
149 | }) | 148 | }) |
150 | 149 | ||
151 | it('Should succeed with a valid HTTP signature', async function () { | 150 | it('Should succeed with a valid HTTP signature', async function () { |
152 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) | 151 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') |
153 | const headers = buildGlobalHeaders(body) | 152 | const headers = buildGlobalHeaders(body) |
154 | 153 | ||
155 | const { statusCode } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) | 154 | const { statusCode } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) |
@@ -168,7 +167,7 @@ describe('Test ActivityPub security', function () { | |||
168 | await killallServers([ servers[1] ]) | 167 | await killallServers([ servers[1] ]) |
169 | await servers[1].run() | 168 | await servers[1].run() |
170 | 169 | ||
171 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) | 170 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce') |
172 | const headers = buildGlobalHeaders(body) | 171 | const headers = buildGlobalHeaders(body) |
173 | 172 | ||
174 | try { | 173 | try { |
@@ -204,7 +203,7 @@ describe('Test ActivityPub security', function () { | |||
204 | body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' | 203 | body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' |
205 | 204 | ||
206 | const signer: any = { privateKey: invalidKeys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } | 205 | const signer: any = { privateKey: invalidKeys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } |
207 | const signedBody = await buildSignedActivity(signer, body) | 206 | const signedBody = await signAndContextify(signer, body, 'Announce') |
208 | 207 | ||
209 | const headers = buildGlobalHeaders(signedBody) | 208 | const headers = buildGlobalHeaders(signedBody) |
210 | 209 | ||
@@ -226,7 +225,7 @@ describe('Test ActivityPub security', function () { | |||
226 | body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' | 225 | body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' |
227 | 226 | ||
228 | const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } | 227 | const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } |
229 | const signedBody = await buildSignedActivity(signer, body) | 228 | const signedBody = await signAndContextify(signer, body, 'Announce') |
230 | 229 | ||
231 | signedBody.actor = 'http://localhost:' + servers[2].port + '/account/peertube' | 230 | signedBody.actor = 'http://localhost:' + servers[2].port + '/account/peertube' |
232 | 231 | ||
@@ -247,7 +246,7 @@ describe('Test ActivityPub security', function () { | |||
247 | body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' | 246 | body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' |
248 | 247 | ||
249 | const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } | 248 | const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } |
250 | const signedBody = await buildSignedActivity(signer, body) | 249 | const signedBody = await signAndContextify(signer, body, 'Announce') |
251 | 250 | ||
252 | const headers = buildGlobalHeaders(signedBody) | 251 | const headers = buildGlobalHeaders(signedBody) |
253 | 252 | ||
@@ -269,7 +268,7 @@ describe('Test ActivityPub security', function () { | |||
269 | body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' | 268 | body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' |
270 | 269 | ||
271 | const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } | 270 | const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } |
272 | const signedBody = await buildSignedActivity(signer, body) | 271 | const signedBody = await signAndContextify(signer, body, 'Announce') |
273 | 272 | ||
274 | const headers = buildGlobalHeaders(signedBody) | 273 | const headers = buildGlobalHeaders(signedBody) |
275 | 274 | ||
diff --git a/server/tests/shared/requests.ts b/server/tests/shared/requests.ts index d7aedf82f..57120caca 100644 --- a/server/tests/shared/requests.ts +++ b/server/tests/shared/requests.ts | |||
@@ -22,7 +22,7 @@ export async function makeFollowRequest (to: { url: string }, by: { url: string, | |||
22 | object: to.url | 22 | object: to.url |
23 | } | 23 | } |
24 | 24 | ||
25 | const body = activityPubContextify(follow) | 25 | const body = activityPubContextify(follow, 'Follow') |
26 | 26 | ||
27 | const httpSignature = { | 27 | const httpSignature = { |
28 | algorithm: HTTP_SIGNATURE.ALGORITHM, | 28 | algorithm: HTTP_SIGNATURE.ALGORITHM, |
diff --git a/shared/models/activitypub/context.ts b/shared/models/activitypub/context.ts index bd795a2fd..4ada3b083 100644 --- a/shared/models/activitypub/context.ts +++ b/shared/models/activitypub/context.ts | |||
@@ -1 +1,15 @@ | |||
1 | export type ContextType = 'All' | 'View' | 'Announce' | 'CacheFile' | 1 | export type ContextType = |
2 | 'Video' | | ||
3 | 'Comment' | | ||
4 | 'Playlist' | | ||
5 | 'Follow' | | ||
6 | 'Reject' | | ||
7 | 'Accept' | | ||
8 | 'View' | | ||
9 | 'Announce' | | ||
10 | 'CacheFile' | | ||
11 | 'Delete' | | ||
12 | 'Rate' | | ||
13 | 'Flag' | | ||
14 | 'Actor' | | ||
15 | 'Collection' | ||
diff --git a/shared/models/activitypub/objects/common-objects.ts b/shared/models/activitypub/objects/common-objects.ts index 43d7f7f74..9bf994379 100644 --- a/shared/models/activitypub/objects/common-objects.ts +++ b/shared/models/activitypub/objects/common-objects.ts | |||
@@ -46,7 +46,7 @@ export type ActivityTrackerUrlObject = { | |||
46 | href: string | 46 | href: string |
47 | } | 47 | } |
48 | 48 | ||
49 | export type ActivityPlaylistInfohashesObject = { | 49 | export type ActivityStreamingPlaylistInfohashesObject = { |
50 | type: 'Infohash' | 50 | type: 'Infohash' |
51 | name: string | 51 | name: string |
52 | } | 52 | } |
@@ -97,7 +97,7 @@ export interface ActivityFlagReasonObject { | |||
97 | 97 | ||
98 | export type ActivityTagObject = | 98 | export type ActivityTagObject = |
99 | ActivityPlaylistSegmentHashesObject | 99 | ActivityPlaylistSegmentHashesObject |
100 | | ActivityPlaylistInfohashesObject | 100 | | ActivityStreamingPlaylistInfohashesObject |
101 | | ActivityVideoUrlObject | 101 | | ActivityVideoUrlObject |
102 | | ActivityHashTagObject | 102 | | ActivityHashTagObject |
103 | | ActivityMentionObject | 103 | | ActivityMentionObject |
diff --git a/shared/models/server/job.model.ts b/shared/models/server/job.model.ts index 91469d010..92d1b5698 100644 --- a/shared/models/server/job.model.ts +++ b/shared/models/server/job.model.ts | |||
@@ -40,9 +40,9 @@ export interface Job { | |||
40 | 40 | ||
41 | export type ActivitypubHttpBroadcastPayload = { | 41 | export type ActivitypubHttpBroadcastPayload = { |
42 | uris: string[] | 42 | uris: string[] |
43 | signatureActorId?: number | 43 | contextType: ContextType |
44 | body: any | 44 | body: any |
45 | contextType?: ContextType | 45 | signatureActorId?: number |
46 | } | 46 | } |
47 | 47 | ||
48 | export type ActivitypubFollowPayload = { | 48 | export type ActivitypubFollowPayload = { |
@@ -62,9 +62,9 @@ export type ActivitypubHttpFetcherPayload = { | |||
62 | 62 | ||
63 | export type ActivitypubHttpUnicastPayload = { | 63 | export type ActivitypubHttpUnicastPayload = { |
64 | uri: string | 64 | uri: string |
65 | contextType: ContextType | ||
65 | signatureActorId?: number | 66 | signatureActorId?: number |
66 | body: object | 67 | body: object |
67 | contextType?: ContextType | ||
68 | } | 68 | } |
69 | 69 | ||
70 | export type RefreshPayload = { | 70 | export type RefreshPayload = { |