From e12a009254de33bcdbd8334992980fa029c3e10d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 19 Dec 2017 10:34:56 +0100 Subject: [PATCH] Status are sent to mastodon --- .../custom-validators/activitypub/actor.ts | 18 +++-- server/initializers/constants.ts | 2 +- .../migrations/0130-video-channel-actor.ts | 10 +-- server/lib/activitypub/actor.ts | 19 +++--- server/lib/activitypub/send/misc.ts | 9 ++- server/lib/activitypub/send/send-accept.ts | 15 +++-- server/lib/activitypub/send/send-create.ts | 13 ++-- server/lib/activitypub/send/send-like.ts | 7 +- server/lib/activitypub/send/send-undo.ts | 7 +- server/lib/activitypub/send/send-update.ts | 10 ++- server/lib/activitypub/share.ts | 3 + .../activitypub-http-broadcast-handler.ts | 6 +- .../activitypub-http-job-scheduler.ts | 26 ++++++- .../activitypub-http-unicast-handler.ts | 6 +- .../video-file-optimizer-handler.ts | 9 ++- .../video-file-transcoder-handler.ts | 5 +- server/middlewares/activitypub.ts | 7 +- server/models/activitypub/actor.ts | 67 +++++++------------ shared/models/activitypub/activity.ts | 1 + yarn.lock | 4 +- 20 files changed, 133 insertions(+), 111 deletions(-) diff --git a/server/helpers/custom-validators/activitypub/actor.ts b/server/helpers/custom-validators/activitypub/actor.ts index 5930bd5da..ec8da3350 100644 --- a/server/helpers/custom-validators/activitypub/actor.ts +++ b/server/helpers/custom-validators/activitypub/actor.ts @@ -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 diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 04b610b7a..4cb54dc93 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -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) // --------------------------------------------------------------------------- diff --git a/server/initializers/migrations/0130-video-channel-actor.ts b/server/initializers/migrations/0130-video-channel-actor.ts index 15b67be81..2e4694a75 100644 --- a/server/initializers/migrations/0130-video-channel-actor.ts +++ b/server/initializers/migrations/0130-video-channel-actor.ts @@ -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 diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index c3de4bdce..ff0a291e8 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts @@ -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 a.id) @@ -133,5 +137,6 @@ export { getOriginVideoAudience, getActorsInvolvedInVideo, getObjectFollowersAudience, - forwardActivity + forwardActivity, + audiencify } diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts index 7579884a7..4eaa329d9 100644 --- a/server/lib/activitypub/send/send-accept.ts +++ b/server/lib/activitypub/send/send-accept.ts @@ -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 } } diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index d26c24838..249dd91dc 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts @@ -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) { diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts index 7e0c73796..743646455 100644 --- a/server/lib/activitypub/send/send-like.ts +++ b/server/lib/activitypub/send/send-like.ts @@ -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) } // --------------------------------------------------------------------------- diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index 92271b700..3a0597fba 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts @@ -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) } diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts index 48bbbcac1..b623fec6c 100644 --- a/server/lib/activitypub/send/send-update.ts +++ b/server/lib/activitypub/send/send-update.ts @@ -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) } diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts index fb01368ec..386ae362a 100644 --- a/server/lib/activitypub/share.ts +++ b/server/lib/activitypub/share.ts @@ -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({ diff --git a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-broadcast-handler.ts b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-broadcast-handler.ts index 8040dde2a..3c4d5556f 100644 --- a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-broadcast-handler.ts +++ b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-broadcast-handler.ts @@ -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) { diff --git a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts index 95a5d3ff2..88885cf97 100644 --- a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts +++ b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts @@ -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 } diff --git a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-unicast-handler.ts b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-unicast-handler.ts index f16cfcec3..7a5caa679 100644 --- a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-unicast-handler.ts +++ b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-unicast-handler.ts @@ -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 { diff --git a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts index 47b12e66f..d905882be 100644 --- a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts +++ b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts @@ -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() diff --git a/server/lib/jobs/transcoding-job-scheduler/video-file-transcoder-handler.ts b/server/lib/jobs/transcoding-job-scheduler/video-file-transcoder-handler.ts index 8957b4565..409123bfe 100644 --- a/server/lib/jobs/transcoding-job-scheduler/video-file-transcoder-handler.ts +++ b/server/lib/jobs/transcoding-job-scheduler/video-file-transcoder-handler.ts @@ -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 } diff --git a/server/middlewares/activitypub.ts b/server/middlewares/activitypub.ts index 37b7c42ec..9113e02a7 100644 --- a/server/middlewares/activitypub.ts +++ b/server/middlewares/activitypub.ts @@ -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() } diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index 8cedcc2bc..e7eb35e2c 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts @@ -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 { 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 { 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 { 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 { 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 { } } - 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 { } } - 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 { 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 { isOwned () { return this.serverId === null } + + getWebfingerUrl () { + return 'acct:' + this.preferredUsername + '@' + this.getHost() + } + + getHost () { + return this.Server ? this.Server.host : CONFIG.WEBSERVER.HOST + } } diff --git a/shared/models/activitypub/activity.ts b/shared/models/activitypub/activity.ts index 1d248d3d7..84f5d851f 100644 --- a/shared/models/activitypub/activity.ts +++ b/shared/models/activitypub/activity.ts @@ -46,6 +46,7 @@ export interface ActivityFollow extends BaseActivity { export interface ActivityAccept extends BaseActivity { type: 'Accept' + object: ActivityFollow } export interface ActivityAnnounce extends BaseActivity { diff --git a/yarn.lock b/yarn.lock index a3f87d381..8f8f8235a 100644 --- 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" -- 2.41.0