Status are sent to mastodon
authorChocobozzz <me@florianbigard.com>
Tue, 19 Dec 2017 09:34:56 +0000 (10:34 +0100)
committerChocobozzz <me@florianbigard.com>
Tue, 19 Dec 2017 09:53:16 +0000 (10:53 +0100)
20 files changed:
server/helpers/custom-validators/activitypub/actor.ts
server/initializers/constants.ts
server/initializers/migrations/0130-video-channel-actor.ts
server/lib/activitypub/actor.ts
server/lib/activitypub/send/misc.ts
server/lib/activitypub/send/send-accept.ts
server/lib/activitypub/send/send-create.ts
server/lib/activitypub/send/send-like.ts
server/lib/activitypub/send/send-undo.ts
server/lib/activitypub/send/send-update.ts
server/lib/activitypub/share.ts
server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-broadcast-handler.ts
server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts
server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-unicast-handler.ts
server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts
server/lib/jobs/transcoding-job-scheduler/video-file-transcoder-handler.ts
server/middlewares/activitypub.ts
server/models/activitypub/actor.ts
shared/models/activitypub/activity.ts
yarn.lock

index 5930bd5daeab0abe90abfe63f7b938ae6fcdc7dd..ec8da33509c27a4dbac6d4f247450e626bae21f1 100644 (file)
@@ -1,8 +1,8 @@
 import * as validator from 'validator'
 import { CONSTRAINTS_FIELDS } from '../../../initializers'
 import { isAccountNameValid } from '../accounts'
-import { exists, isUUIDValid } from '../misc'
-import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels'
+import { exists } from '../misc'
+import { isVideoChannelNameValid } from '../video-channels'
 import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc'
 
 function isActorEndpointsObjectValid (endpointObject: any) {
@@ -23,41 +23,39 @@ function isActorPublicKeyValid (publicKey: string) {
   return exists(publicKey) &&
     typeof publicKey === 'string' &&
     publicKey.startsWith('-----BEGIN PUBLIC KEY-----') &&
-    publicKey.endsWith('-----END PUBLIC KEY-----') &&
+    publicKey.indexOf('-----END PUBLIC KEY-----') !== -1 &&
     validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACTOR.PUBLIC_KEY)
 }
 
+const actorNameRegExp = new RegExp('[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_]+')
 function isActorPreferredUsernameValid (preferredUsername: string) {
-  return isAccountNameValid(preferredUsername) || isVideoChannelNameValid(preferredUsername)
+  return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp)
 }
 
-const actorNameRegExp = new RegExp('[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_]+')
 function isActorNameValid (name: string) {
-  return exists(name) && validator.matches(name, actorNameRegExp)
+  return isAccountNameValid(name) || isVideoChannelNameValid(name)
 }
 
 function isActorPrivateKeyValid (privateKey: string) {
   return exists(privateKey) &&
     typeof privateKey === 'string' &&
     privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----') &&
-    privateKey.endsWith('-----END RSA PRIVATE KEY-----') &&
+    // Sometimes there is a \n at the end, so just assert the string contains the end mark
+    privateKey.indexOf('-----END RSA PRIVATE KEY-----') !== -1 &&
     validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACTOR.PRIVATE_KEY)
 }
 
 function isRemoteActorValid (remoteActor: any) {
   return isActivityPubUrlValid(remoteActor.id) &&
-    isUUIDValid(remoteActor.uuid) &&
     isActorTypeValid(remoteActor.type) &&
     isActivityPubUrlValid(remoteActor.following) &&
     isActivityPubUrlValid(remoteActor.followers) &&
     isActivityPubUrlValid(remoteActor.inbox) &&
     isActivityPubUrlValid(remoteActor.outbox) &&
-    isActorNameValid(remoteActor.name) &&
     isActorPreferredUsernameValid(remoteActor.preferredUsername) &&
     isActivityPubUrlValid(remoteActor.url) &&
     isActorPublicKeyObjectValid(remoteActor.publicKey) &&
     isActorEndpointsObjectValid(remoteActor.endpoints) &&
-    (!remoteActor.summary || isVideoChannelDescriptionValid(remoteActor.summary)) &&
     setValidAttributedTo(remoteActor) &&
     // If this is not an account, it should be attributed to an account
     // In PeerTube we use this to attach a video channel to a specific account
index 04b610b7a4b822ed4941a0db02fa689455a1b47b..4cb54dc93b5370ec266125384785e09d773c06cc 100644 (file)
@@ -316,7 +316,7 @@ const CACHE = {
   }
 }
 
-const ACCEPT_HEADERS = ACTIVITY_PUB.POTENTIAL_ACCEPT_HEADERS.concat('html', 'application/json')
+const ACCEPT_HEADERS = [ 'html', 'application/json' ].concat(ACTIVITY_PUB.POTENTIAL_ACCEPT_HEADERS)
 
 // ---------------------------------------------------------------------------
 
index 15b67be81e2d6434004a25751607c122259b9cb1..2e4694a7524007e98f2b35054f4d8d41af0aa69e 100644 (file)
@@ -22,7 +22,7 @@ async function up (utils: {
         id integer NOT NULL,
         type enum_actor_type NOT NULL,
         uuid uuid NOT NULL,
-        name character varying(255) NOT NULL,
+        "preferredUsername" character varying(255) NOT NULL,
         url character varying(2000) NOT NULL,
         "publicKey" character varying(5000),
         "privateKey" character varying(5000),
@@ -50,7 +50,7 @@ async function up (utils: {
       `ALTER SEQUENCE actor_id_seq OWNED BY actor.id`,
       `ALTER TABLE ONLY actor ALTER COLUMN id SET DEFAULT nextval('actor_id_seq'::regclass)`,
       `ALTER TABLE ONLY actor ADD CONSTRAINT actor_pkey PRIMARY KEY (id);`,
-      `CREATE UNIQUE INDEX actor_name_server_id ON actor USING btree (name, "serverId")`,
+      `CREATE UNIQUE INDEX actor_preferred_username_server_id ON actor USING btree ("preferredUsername", "serverId")`,
       `ALTER TABLE ONLY actor
         ADD CONSTRAINT "actor_avatarId_fkey" FOREIGN KEY ("avatarId") REFERENCES avatar(id) ON UPDATE CASCADE ON DELETE CASCADE`,
       `ALTER TABLE ONLY actor
@@ -68,7 +68,7 @@ async function up (utils: {
       `
       INSERT INTO "actor"
         (
-          type, uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
+          type, uuid, "preferredUsername", url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
           "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
         )
         SELECT 
@@ -83,7 +83,7 @@ async function up (utils: {
       `
       INSERT INTO "actor"
         (
-          type, uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
+          type, uuid, "preferredUsername", url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
           "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
         )
         SELECT 
@@ -119,7 +119,7 @@ async function up (utils: {
     const query = `  
     INSERT INTO actor 
     (
-    type, uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl", 
+    type, uuid, "preferredUsername", url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl", 
     "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
     )
     SELECT 
index c3de4bdce9414461af83c58e65928e9ce8967a54..ff0a291e840bc920ad5e737073781719a733a264 100644 (file)
@@ -11,7 +11,7 @@ import { ActorModel } from '../../models/activitypub/actor'
 import { ServerModel } from '../../models/server/server'
 import { VideoChannelModel } from '../../models/video/video-channel'
 
-  // Set account keys, this could be long so process after the account creation and do not block the client
+// Set account keys, this could be long so process after the account creation and do not block the client
 function setAsyncActorKeys (actor: ActorModel) {
   return createPrivateAndPublicKeys()
     .then(({ publicKey, privateKey }) => {
@@ -107,7 +107,7 @@ function saveActorAndServerAndModelIfNotExist (
 
 type FetchRemoteActorResult = {
   actor: ActorModel
-  preferredUsername: string
+  name: string
   summary: string
   attributedTo: ActivityPubAttributedTo[]
 }
@@ -142,8 +142,8 @@ async function fetchRemoteActor (actorUrl: string): Promise<FetchRemoteActorResu
   const actor = new ActorModel({
     type: actorJSON.type,
     uuid: actorJSON.uuid,
-    name: actorJSON.name,
-    url: actorJSON.url,
+    preferredUsername: actorJSON.preferredUsername,
+    url: actorJSON.id,
     publicKey: actorJSON.publicKey.publicKeyPem,
     privateKey: null,
     followersCount: followersCount,
@@ -155,19 +155,20 @@ async function fetchRemoteActor (actorUrl: string): Promise<FetchRemoteActorResu
     followingUrl: actorJSON.following
   })
 
+  const name = actorJSON.name || actorJSON.preferredUsername
   return {
     actor,
-    preferredUsername: actorJSON.preferredUsername,
+    name,
     summary: actorJSON.summary,
     attributedTo: actorJSON.attributedTo
   }
 }
 
-function buildActorInstance (type: ActivityPubActorType, url: string, name: string, uuid?: string) {
+function buildActorInstance (type: ActivityPubActorType, url: string, preferredUsername: string, uuid?: string) {
   return new ActorModel({
     type,
     url,
-    name,
+    preferredUsername,
     uuid,
     publicKey: null,
     privateKey: null,
@@ -210,7 +211,7 @@ async function fetchActorTotalItems (url: string) {
 
 function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t: Transaction) {
   const account = new AccountModel({
-    name: result.preferredUsername,
+    name: result.name,
     actorId: actor.id
   })
 
@@ -219,7 +220,7 @@ function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t: Tran
 
 async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResult, ownerActor: ActorModel, t: Transaction) {
   const videoChannel = new VideoChannelModel({
-    name: result.preferredUsername,
+    name: result.name,
     description: result.summary,
     actorId: actor.id,
     accountId: ownerActor.Account.id
index 14101e63006db08b4af776f98564e10414cffff8..2dc8d3d5915a7bfde2dc37fffb71464ebd59fca4 100644 (file)
@@ -1,5 +1,5 @@
 import { Transaction } from 'sequelize'
-import { Activity } from '../../../../shared/models/activitypub'
+import { Activity, ActivityAudience } from '../../../../shared/models/activitypub'
 import { logger } from '../../../helpers'
 import { ACTIVITY_PUB } from '../../../initializers'
 import { ActorModel } from '../../../models/activitypub/actor'
@@ -116,6 +116,10 @@ async function getAudience (actorSender: ActorModel, t: Transaction, isPublic =
   return { to, cc }
 }
 
+function audiencify (object: any, audience: ActivityAudience) {
+  return Object.assign(object, audience)
+}
+
 async function computeFollowerUris (toActorFollower: ActorModel[], followersException: ActorModel[], t: Transaction) {
   const toActorFollowerIds = toActorFollower.map(a => a.id)
 
@@ -133,5 +137,6 @@ export {
   getOriginVideoAudience,
   getActorsInvolvedInVideo,
   getObjectFollowersAudience,
-  forwardActivity
+  forwardActivity,
+  audiencify
 }
index 7579884a79ca43f241a5d325e9bfda903f1918e9..4eaa329d935cf81e2affa7b8710e891ec238eab1 100644 (file)
@@ -1,16 +1,20 @@
 import { Transaction } from 'sequelize'
-import { ActivityAccept } from '../../../../shared/models/activitypub'
+import { ActivityAccept, ActivityFollow } from '../../../../shared/models/activitypub'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
-import { getActorFollowAcceptActivityPubUrl } from '../url'
+import { getActorFollowAcceptActivityPubUrl, getActorFollowActivityPubUrl } from '../url'
 import { unicastTo } from './misc'
+import { followActivityData } from './send-follow'
 
 async function sendAccept (actorFollow: ActorFollowModel, t: Transaction) {
   const follower = actorFollow.ActorFollower
   const me = actorFollow.ActorFollowing
 
+  const followUrl = getActorFollowActivityPubUrl(actorFollow)
+  const followData = followActivityData(followUrl, follower, me)
+
   const url = getActorFollowAcceptActivityPubUrl(actorFollow)
-  const data = acceptActivityData(url, me)
+  const data = acceptActivityData(url, me, followData)
 
   return unicastTo(data, me, follower.inboxUrl, t)
 }
@@ -23,10 +27,11 @@ export {
 
 // ---------------------------------------------------------------------------
 
-function acceptActivityData (url: string, byActor: ActorModel): ActivityAccept {
+function acceptActivityData (url: string, byActor: ActorModel, followActivityData: ActivityFollow): ActivityAccept {
   return {
     type: 'Accept',
     id: url,
-    actor: byActor.url
+    actor: byActor.url,
+    object: followActivityData
   }
 }
index d26c24838780cf20122c21db910a70108ddbd127..249dd91dc846155f1dcb821621fe3a480a198812 100644 (file)
@@ -7,6 +7,7 @@ import { VideoModel } from '../../../models/video/video'
 import { VideoAbuseModel } from '../../../models/video/video-abuse'
 import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
 import {
+  audiencify,
   broadcastToFollowers,
   getActorsInvolvedInVideo,
   getAudience,
@@ -16,9 +17,11 @@ import {
 } from './misc'
 
 async function sendCreateVideo (video: VideoModel, t: Transaction) {
-  const byActor = video.VideoChannel.Account.Actor
+  if (video.privacy === VideoPrivacy.PRIVATE) return
 
+  const byActor = video.VideoChannel.Account.Actor
   const videoObject = video.toActivityPubObject()
+
   const audience = await getAudience(byActor, t, video.privacy === VideoPrivacy.PUBLIC)
   const data = await createActivityData(video.url, byActor, videoObject, t, audience)
 
@@ -93,14 +96,12 @@ async function createActivityData (
     audience = await getAudience(byActor, t)
   }
 
-  return {
+  return audiencify({
     type: 'Create',
     id: url,
     actor: byActor.url,
-    to: audience.to,
-    cc: audience.cc,
-    object
-  }
+    object: audiencify(object, audience)
+  }, audience)
 }
 
 function createDislikeActivityData (byActor: ActorModel, video: VideoModel) {
index 7e0c73796525fe322d6e5867ee0a1f72501ebbe2..743646455e7d751b564f44cdaf3e27ac977188e4 100644 (file)
@@ -4,6 +4,7 @@ import { ActorModel } from '../../../models/activitypub/actor'
 import { VideoModel } from '../../../models/video/video'
 import { getVideoLikeActivityPubUrl } from '../url'
 import {
+  audiencify,
   broadcastToFollowers,
   getActorsInvolvedInVideo,
   getAudience,
@@ -44,14 +45,12 @@ async function likeActivityData (
     audience = await getAudience(byActor, t)
   }
 
-  return {
+  return audiencify({
     type: 'Like',
     id: url,
     actor: byActor.url,
-    to: audience.to,
-    cc: audience.cc,
     object: video.url
-  }
+  }, audience)
 }
 
 // ---------------------------------------------------------------------------
index 92271b700138ba57960a2d7cd26772d487ed2f8a..3a0597fbae6078ddc17ae0573ec4b85f5353a7b6 100644 (file)
@@ -11,6 +11,7 @@ import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
 import { VideoModel } from '../../../models/video/video'
 import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
 import {
+  audiencify,
   broadcastToFollowers,
   getActorsInvolvedInVideo,
   getAudience,
@@ -112,12 +113,10 @@ async function undoActivityData (
     audience = await getAudience(byActor, t)
   }
 
-  return {
+  return audiencify({
     type: 'Undo',
     id: url,
     actor: byActor.url,
-    to: audience.to,
-    cc: audience.cc,
     object
-  }
+  }, audience)
 }
index 48bbbcac18b0fa51baec7a3799465081046b252a..b623fec6c22bd76a69a8a17d598a18bee4ca49bb 100644 (file)
@@ -5,7 +5,7 @@ import { ActorModel } from '../../../models/activitypub/actor'
 import { VideoModel } from '../../../models/video/video'
 import { VideoShareModel } from '../../../models/video/video-share'
 import { getUpdateActivityPubUrl } from '../url'
-import { broadcastToFollowers, getAudience } from './misc'
+import { audiencify, broadcastToFollowers, getAudience } from './misc'
 
 async function sendUpdateVideo (video: VideoModel, t: Transaction) {
   const byActor = video.VideoChannel.Account.Actor
@@ -41,12 +41,10 @@ async function updateActivityData (
     audience = await getAudience(byActor, t)
   }
 
-  return {
+  return audiencify({
     type: 'Update',
     id: url,
     actor: byActor.url,
-    to: audience.to,
-    cc: audience.cc,
-    object
-  }
+    object: audiencify(object, audience)
+  }, audience)
 }
index fb01368ec8c2351c1aced31632d08b556ff58b3a..386ae362a402253a138ed17b7f8667d0139efe77 100644 (file)
@@ -1,10 +1,13 @@
 import { Transaction } from 'sequelize'
+import { VideoPrivacy } from '../../../shared/models/videos'
 import { getServerActor } from '../../helpers'
 import { VideoModel } from '../../models/video/video'
 import { VideoShareModel } from '../../models/video/video-share'
 import { sendVideoAnnounceToFollowers } from './send'
 
 async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) {
+  if (video.privacy === VideoPrivacy.PRIVATE) return
+
   const serverActor = await getServerActor()
 
   const serverShare = VideoShareModel.create({
index 8040dde2ae50a791763dc1f467c9f4127bf045d6..3c4d5556fa24426666a7912c3fe35edae4702540 100644 (file)
@@ -1,15 +1,17 @@
 import { doRequest, logger } from '../../../helpers'
-import { ActivityPubHttpPayload, computeBody, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
+import { ActivityPubHttpPayload, buildSignedRequestOptions, computeBody, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
 
 async function process (payload: ActivityPubHttpPayload, jobId: number) {
   logger.info('Processing ActivityPub broadcast in job %d.', jobId)
 
   const body = await computeBody(payload)
+  const httpSignatureOptions = await buildSignedRequestOptions(payload)
 
   const options = {
     method: 'POST',
     uri: '',
-    json: body
+    json: body,
+    httpSignature: httpSignatureOptions
   }
 
   for (const uri of payload.uris) {
index 95a5d3ff201a6019c807011a90c10dc0e44a0679..88885cf9723cfa3d196cfb3f5a6a22da4ac02ffa 100644 (file)
@@ -1,5 +1,5 @@
 import { JobCategory } from '../../../../shared'
-import { buildSignedActivity, logger } from '../../../helpers'
+import { buildSignedActivity, getServerActor, logger } from '../../../helpers'
 import { ACTIVITY_PUB } from '../../../initializers'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { JobHandler, JobScheduler } from '../job-scheduler'
@@ -46,16 +46,36 @@ async function computeBody (payload: ActivityPubHttpPayload) {
 
   if (payload.signatureActorId) {
     const actorSignature = await ActorModel.load(payload.signatureActorId)
-    if (!actorSignature) throw new Error('Unknown signature account id.')
+    if (!actorSignature) throw new Error('Unknown signature actor id.')
     body = await buildSignedActivity(actorSignature, payload.body)
   }
 
   return body
 }
 
+async function buildSignedRequestOptions (payload: ActivityPubHttpPayload) {
+  let actor: ActorModel
+  if (payload.signatureActorId) {
+    actor = await ActorModel.load(payload.signatureActorId)
+    if (!actor) throw new Error('Unknown signature actor id.')
+  } else {
+    // We need to sign the request, so use the server
+    actor = await getServerActor()
+  }
+
+  const keyId = actor.getWebfingerUrl()
+  return {
+    algorithm: 'rsa-sha256',
+    authorizationHeaderName: 'Signature',
+    keyId,
+    key: actor.privateKey
+  }
+}
+
 export {
   ActivityPubHttpPayload,
   activitypubHttpJobScheduler,
   maybeRetryRequestLater,
-  computeBody
+  computeBody,
+  buildSignedRequestOptions
 }
index f16cfcec3d898a7793bc3913eb6b9b9528f08960..7a5caa679978f18a1141e5e3993e292da8586c76 100644 (file)
@@ -1,16 +1,18 @@
 import { doRequest, logger } from '../../../helpers'
-import { ActivityPubHttpPayload, computeBody, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
+import { ActivityPubHttpPayload, buildSignedRequestOptions, computeBody, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
 
 async function process (payload: ActivityPubHttpPayload, jobId: number) {
   logger.info('Processing ActivityPub unicast in job %d.', jobId)
 
   const body = await computeBody(payload)
+  const httpSignatureOptions = await buildSignedRequestOptions(payload)
 
   const uri = payload.uris[0]
   const options = {
     method: 'POST',
     uri,
-    json: body
+    json: body,
+    httpSignature: httpSignatureOptions
   }
 
   try {
index 47b12e66f2999a5de4efd25692b739380644c796..d905882be510721b9a8070a0203a1c66a602ba34 100644 (file)
@@ -1,4 +1,5 @@
 import * as Bluebird from 'bluebird'
+import { VideoPrivacy } from '../../../../shared/models/videos'
 import { computeResolutionsToTranscode, logger } from '../../../helpers'
 import { sequelizeTypescript } from '../../../initializers'
 import { VideoModel } from '../../../models/video/video'
@@ -35,9 +36,11 @@ async function onSuccess (jobId: number, video: VideoModel, jobScheduler: JobSch
   // Video does not exist anymore
   if (!videoDatabase) return undefined
 
-  // Now we'll add the video's meta data to our followers
-  await sendCreateVideo(video, undefined)
-  await shareVideoByServerAndChannel(video, undefined)
+  if (video.privacy !== VideoPrivacy.PRIVATE) {
+    // Now we'll add the video's meta data to our followers
+    await sendCreateVideo(video, undefined)
+    await shareVideoByServerAndChannel(video, undefined)
+  }
 
   const originalFileHeight = await videoDatabase.getOriginalFileHeight()
 
index 8957b4565014ee4269dc21a69c6b1f45c3ad9628..409123bfe47deeaebe64a0cdec0cb53c43d7c69c 100644 (file)
@@ -1,4 +1,5 @@
 import { VideoResolution } from '../../../../shared'
+import { VideoPrivacy } from '../../../../shared/models/videos'
 import { logger } from '../../../helpers'
 import { VideoModel } from '../../../models/video/video'
 import { sendUpdateVideo } from '../../activitypub/send'
@@ -31,7 +32,9 @@ async function onSuccess (jobId: number, video: VideoModel) {
   // Video does not exist anymore
   if (!videoDatabase) return undefined
 
-  await sendUpdateVideo(video, undefined)
+  if (video.privacy !== VideoPrivacy.PRIVATE) {
+    await sendUpdateVideo(video, undefined)
+  }
 
   return undefined
 }
index 37b7c42ec3c49db64e4763202728cd5a27e8001d..9113e02a738a32f379bdf160874ee1fbe06560df 100644 (file)
@@ -9,11 +9,13 @@ import { ActorModel } from '../models/activitypub/actor'
 async function checkSignature (req: Request, res: Response, next: NextFunction) {
   const signatureObject: ActivityPubSignature = req.body.signature
 
-  logger.debug('Checking signature of actor %s...', signatureObject.creator)
+  const [ creator ] = signatureObject.creator.split('#')
+
+  logger.debug('Checking signature of actor %s...', creator)
 
   let actor: ActorModel
   try {
-    actor = await getOrCreateActorAndServerAndModel(signatureObject.creator)
+    actor = await getOrCreateActorAndServerAndModel(creator)
   } catch (err) {
     logger.error('Cannot create remote actor and check signature.', err)
     return res.sendStatus(403)
@@ -32,6 +34,7 @@ async function checkSignature (req: Request, res: Response, next: NextFunction)
 function executeIfActivityPub (fun: RequestHandler | RequestHandler[]) {
   return (req: Request, res: Response, next: NextFunction) => {
     const accepted = req.accepts(ACCEPT_HEADERS)
+    console.log(accepted)
     if (accepted === false || ACTIVITY_PUB.POTENTIAL_ACCEPT_HEADERS.indexOf(accepted) === -1) {
       return next()
     }
index 8cedcc2bc7dafa41be9802f6ec8a6e12c6511342..e7eb35e2c000637ceb929a4de0552a764d7b9933 100644 (file)
@@ -2,32 +2,15 @@ import { values } from 'lodash'
 import { join } from 'path'
 import * as Sequelize from 'sequelize'
 import {
-  AllowNull,
-  BelongsTo,
-  Column,
-  CreatedAt,
-  DataType,
-  Default, DefaultScope,
-  ForeignKey,
-  HasMany,
-  HasOne,
-  Is,
-  IsUUID,
-  Model,
-  Scopes,
-  Table,
-  UpdatedAt
+  AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, DefaultScope, ForeignKey, HasMany, HasOne, Is, IsUUID, Model, Scopes,
+  Table, UpdatedAt
 } from 'sequelize-typescript'
 import { ActivityPubActorType } from '../../../shared/models/activitypub'
 import { Avatar } from '../../../shared/models/avatars/avatar.model'
 import { activityPubContextify } from '../../helpers'
 import {
-  isActivityPubUrlValid,
-  isActorFollowersCountValid,
-  isActorFollowingCountValid,
-  isActorNameValid,
-  isActorPrivateKeyValid,
-  isActorPublicKeyValid
+  isActivityPubUrlValid, isActorFollowersCountValid, isActorFollowingCountValid, isActorPreferredUsernameValid,
+  isActorPrivateKeyValid, isActorPublicKeyValid
 } from '../../helpers/custom-validators/activitypub'
 import { ACTIVITY_PUB_ACTOR_TYPES, AVATARS_DIR, CONFIG, CONSTRAINTS_FIELDS } from '../../initializers'
 import { AccountModel } from '../account/account'
@@ -71,7 +54,7 @@ enum ScopeNames {
   tableName: 'actor',
   indexes: [
     {
-      fields: [ 'name', 'serverId' ],
+      fields: [ 'preferredUsername', 'serverId' ],
       unique: true
     }
   ]
@@ -89,9 +72,9 @@ export class ActorModel extends Model<ActorModel> {
   uuid: string
 
   @AllowNull(false)
-  @Is('ActorName', value => throwIfNotValid(value, isActorNameValid, 'actor name'))
+  @Is('ActorPreferredUsername', value => throwIfNotValid(value, isActorPreferredUsernameValid, 'actor preferred username'))
   @Column
-  name: string
+  preferredUsername: string
 
   @AllowNull(false)
   @Is('ActorUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
@@ -212,16 +195,6 @@ export class ActorModel extends Model<ActorModel> {
     return ActorModel.scope(ScopeNames.FULL).findById(id)
   }
 
-  static loadByUUID (uuid: string) {
-    const query = {
-      where: {
-        uuid
-      }
-    }
-
-    return ActorModel.scope(ScopeNames.FULL).findOne(query)
-  }
-
   static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) {
     const query = {
       where: {
@@ -235,10 +208,10 @@ export class ActorModel extends Model<ActorModel> {
     return ActorModel.scope(ScopeNames.FULL).findAll(query)
   }
 
-  static loadLocalByName (name: string) {
+  static loadLocalByName (preferredUsername: string) {
     const query = {
       where: {
-        name,
+        preferredUsername,
         serverId: null
       }
     }
@@ -246,10 +219,10 @@ export class ActorModel extends Model<ActorModel> {
     return ActorModel.scope(ScopeNames.FULL).findOne(query)
   }
 
-  static loadByNameAndHost (name: string, host: string) {
+  static loadByNameAndHost (preferredUsername: string, host: string) {
     const query = {
       where: {
-        name
+        preferredUsername
       },
       include: [
         {
@@ -286,17 +259,15 @@ export class ActorModel extends Model<ActorModel> {
       }
     }
 
-    let host = CONFIG.WEBSERVER.HOST
     let score: number
     if (this.Server) {
-      host = this.Server.host
       score = this.Server.score
     }
 
     return {
       id: this.id,
       uuid: this.uuid,
-      host,
+      host: this.getHost(),
       score,
       followingCount: this.followingCount,
       followersCount: this.followersCount,
@@ -304,7 +275,7 @@ export class ActorModel extends Model<ActorModel> {
     }
   }
 
-  toActivityPubObject (preferredUsername: string, type: 'Account' | 'Application' | 'VideoChannel') {
+  toActivityPubObject (name: string, type: 'Account' | 'Application' | 'VideoChannel') {
     let activityPubType
     if (type === 'Account') {
       activityPubType = 'Person' as 'Person'
@@ -321,9 +292,9 @@ export class ActorModel extends Model<ActorModel> {
       followers: this.getFollowersUrl(),
       inbox: this.inboxUrl,
       outbox: this.outboxUrl,
-      preferredUsername,
+      preferredUsername: this.preferredUsername,
       url: this.url,
-      name: this.name,
+      name,
       endpoints: {
         sharedInbox: this.sharedInboxUrl
       },
@@ -373,4 +344,12 @@ export class ActorModel extends Model<ActorModel> {
   isOwned () {
     return this.serverId === null
   }
+
+  getWebfingerUrl () {
+    return 'acct:' + this.preferredUsername + '@' + this.getHost()
+  }
+
+  getHost () {
+    return this.Server ? this.Server.host : CONFIG.WEBSERVER.HOST
+  }
 }
index 1d248d3d7423a8b453aabae3f6b8042e2b70d09d..84f5d851f7445489af4a1147c7656d02bec4b15e 100644 (file)
@@ -46,6 +46,7 @@ export interface ActivityFollow extends BaseActivity {
 
 export interface ActivityAccept extends BaseActivity {
   type: 'Accept'
+  object: ActivityFollow
 }
 
 export interface ActivityAnnounce extends BaseActivity {
index a3f87d3817b24b32ea5be8cf1f86142a363dc274..8f8f8235ae6f67841e52f8140728c7a69543fdce 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -2378,9 +2378,9 @@ jsonify@~0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
 
-"jsonld-signatures@https://github.com/digitalbazaar/jsonld-signatures#rsa2017":
+"jsonld-signatures@https://github.com/Chocobozzz/jsonld-signatures#rsa2017":
   version "1.2.2-2"
-  resolved "https://github.com/digitalbazaar/jsonld-signatures#ccb5ca156d72d7632131080d6ef564681791391e"
+  resolved "https://github.com/Chocobozzz/jsonld-signatures#77660963e722eb4541d2d255f9d9d4216329665f"
   dependencies:
     bitcore-message "github:CoMakery/bitcore-message#dist"
     jsonld "^0.5.12"