aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/activitypub
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-07-27 11:05:32 +0200
committerChocobozzz <me@florianbigard.com>2022-07-27 13:52:13 +0200
commit073deef8862f462de5f159a57877ef415ebe4c69 (patch)
tree256d5ff4483ef68b07754f767626a72ac793bd5f /server/lib/activitypub
parent3267d381f4fdd128b2f948670b2e2ba765145276 (diff)
downloadPeerTube-073deef8862f462de5f159a57877ef415ebe4c69.tar.gz
PeerTube-073deef8862f462de5f159a57877ef415ebe4c69.tar.zst
PeerTube-073deef8862f462de5f159a57877ef415ebe4c69.zip
Handle rejected follows in client
Also add quick filters so it's easier to find pending follows
Diffstat (limited to 'server/lib/activitypub')
-rw-r--r--server/lib/activitypub/process/process-follow.ts108
1 files changed, 78 insertions, 30 deletions
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts
index a1958f464..e633cd3ae 100644
--- a/server/lib/activitypub/process/process-follow.ts
+++ b/server/lib/activitypub/process/process-follow.ts
@@ -1,3 +1,6 @@
1import { Transaction } from 'sequelize/types'
2import { isBlockedByServerOrAccount } from '@server/lib/blocklist'
3import { AccountModel } from '@server/models/account/account'
1import { getServerActor } from '@server/models/application/application' 4import { getServerActor } from '@server/models/application/application'
2import { ActivityFollow } from '../../../../shared/models/activitypub' 5import { ActivityFollow } from '../../../../shared/models/activitypub'
3import { retryTransactionWrapper } from '../../../helpers/database-utils' 6import { retryTransactionWrapper } from '../../../helpers/database-utils'
@@ -8,7 +11,7 @@ import { getAPId } from '../../../lib/activitypub/activity'
8import { ActorModel } from '../../../models/actor/actor' 11import { ActorModel } from '../../../models/actor/actor'
9import { ActorFollowModel } from '../../../models/actor/actor-follow' 12import { ActorFollowModel } from '../../../models/actor/actor-follow'
10import { APProcessorOptions } from '../../../types/activitypub-processor.model' 13import { APProcessorOptions } from '../../../types/activitypub-processor.model'
11import { MActorFollowActors, MActorSignature } from '../../../types/models' 14import { MActorFollow, MActorFull, MActorId, MActorSignature } from '../../../types/models'
12import { Notifier } from '../../notifier' 15import { Notifier } from '../../notifier'
13import { autoFollowBackIfNeeded } from '../follow' 16import { autoFollowBackIfNeeded } from '../follow'
14import { sendAccept, sendReject } from '../send' 17import { sendAccept, sendReject } from '../send'
@@ -31,22 +34,14 @@ export {
31// --------------------------------------------------------------------------- 34// ---------------------------------------------------------------------------
32 35
33async function processFollow (byActor: MActorSignature, activityId: string, targetActorURL: string) { 36async function processFollow (byActor: MActorSignature, activityId: string, targetActorURL: string) {
34 const { actorFollow, created, isFollowingInstance, targetActor } = await sequelizeTypescript.transaction(async t => { 37 const { actorFollow, created, targetActor } = await sequelizeTypescript.transaction(async t => {
35 const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t) 38 const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)
36 39
37 if (!targetActor) throw new Error('Unknown actor') 40 if (!targetActor) throw new Error('Unknown actor')
38 if (targetActor.isOwned() === false) throw new Error('This is not a local actor.') 41 if (targetActor.isOwned() === false) throw new Error('This is not a local actor.')
39 42
40 const serverActor = await getServerActor() 43 if (rejectIfInstanceFollowDisabled(byActor, activityId, targetActor)) return { actorFollow: undefined }
41 const isFollowingInstance = targetActor.id === serverActor.id 44 if (await rejectIfMuted(byActor, activityId, targetActor)) return { actorFollow: undefined }
42
43 if (isFollowingInstance && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
44 logger.info('Rejecting %s because instance followers are disabled.', targetActor.url)
45
46 sendReject(activityId, byActor, targetActor)
47
48 return { actorFollow: undefined as MActorFollowActors }
49 }
50 45
51 const [ actorFollow, created ] = await ActorFollowModel.findOrCreateCustom({ 46 const [ actorFollow, created ] = await ActorFollowModel.findOrCreateCustom({
52 byActor, 47 byActor,
@@ -58,24 +53,11 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
58 transaction: t 53 transaction: t
59 }) 54 })
60 55
61 // Already rejected 56 if (rejectIfAlreadyRejected(actorFollow, byActor, activityId, targetActor)) return { actorFollow: undefined }
62 if (actorFollow.state === 'rejected') {
63 return { actorFollow: undefined as MActorFollowActors }
64 }
65
66 // Set the follow as accepted if the remote actor follows a channel or account
67 // Or if the instance automatically accepts followers
68 if (actorFollow.state !== 'accepted' && (isFollowingInstance === false || CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false)) {
69 actorFollow.state = 'accepted'
70 57
71 await actorFollow.save({ transaction: t }) 58 await acceptIfNeeded(actorFollow, targetActor, t)
72 }
73 59
74 // Before PeerTube V3 we did not save the follow ID. Try to fix these old follows 60 await fixFollowURLIfNeeded(actorFollow, activityId, t)
75 if (!actorFollow.url) {
76 actorFollow.url = activityId
77 await actorFollow.save({ transaction: t })
78 }
79 61
80 actorFollow.ActorFollower = byActor 62 actorFollow.ActorFollower = byActor
81 actorFollow.ActorFollowing = targetActor 63 actorFollow.ActorFollowing = targetActor
@@ -87,7 +69,7 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
87 await autoFollowBackIfNeeded(actorFollow, t) 69 await autoFollowBackIfNeeded(actorFollow, t)
88 } 70 }
89 71
90 return { actorFollow, created, isFollowingInstance, targetActor } 72 return { actorFollow, created, targetActor }
91 }) 73 })
92 74
93 // Rejected 75 // Rejected
@@ -97,7 +79,7 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
97 const follower = await ActorModel.loadFull(byActor.id) 79 const follower = await ActorModel.loadFull(byActor.id)
98 const actorFollowFull = Object.assign(actorFollow, { ActorFollowing: targetActor, ActorFollower: follower }) 80 const actorFollowFull = Object.assign(actorFollow, { ActorFollowing: targetActor, ActorFollower: follower })
99 81
100 if (isFollowingInstance) { 82 if (isFollowingInstance(targetActor)) {
101 Notifier.Instance.notifyOfNewInstanceFollow(actorFollowFull) 83 Notifier.Instance.notifyOfNewInstanceFollow(actorFollowFull)
102 } else { 84 } else {
103 Notifier.Instance.notifyOfNewUserFollow(actorFollowFull) 85 Notifier.Instance.notifyOfNewUserFollow(actorFollowFull)
@@ -106,3 +88,69 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
106 88
107 logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url) 89 logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url)
108} 90}
91
92function rejectIfInstanceFollowDisabled (byActor: MActorSignature, activityId: string, targetActor: MActorFull) {
93 if (isFollowingInstance(targetActor) && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
94 logger.info('Rejecting %s because instance followers are disabled.', targetActor.url)
95
96 sendReject(activityId, byActor, targetActor)
97
98 return true
99 }
100
101 return false
102}
103
104async function rejectIfMuted (byActor: MActorSignature, activityId: string, targetActor: MActorFull) {
105 const followerAccount = await AccountModel.load(byActor.Account.id)
106 const followingAccountId = targetActor.Account
107
108 if (followerAccount && await isBlockedByServerOrAccount(followerAccount, followingAccountId)) {
109 logger.info('Rejecting %s because follower is muted.', byActor.url)
110
111 sendReject(activityId, byActor, targetActor)
112
113 return true
114 }
115
116 return false
117}
118
119function rejectIfAlreadyRejected (actorFollow: MActorFollow, byActor: MActorSignature, activityId: string, targetActor: MActorFull) {
120 // Already rejected
121 if (actorFollow.state === 'rejected') {
122 logger.info('Rejecting %s because follow is already rejected.', byActor.url)
123
124 sendReject(activityId, byActor, targetActor)
125
126 return true
127 }
128
129 return false
130}
131
132async function acceptIfNeeded (actorFollow: MActorFollow, targetActor: MActorFull, transaction: Transaction) {
133 // Set the follow as accepted if the remote actor follows a channel or account
134 // Or if the instance automatically accepts followers
135 if (actorFollow.state === 'accepted') return
136 if (!isFollowingInstance(targetActor)) return
137 if (CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === true) return
138
139 actorFollow.state = 'accepted'
140
141 await actorFollow.save({ transaction })
142}
143
144async function fixFollowURLIfNeeded (actorFollow: MActorFollow, activityId: string, transaction: Transaction) {
145 // Before PeerTube V3 we did not save the follow ID. Try to fix these old follows
146 if (!actorFollow.url) {
147 actorFollow.url = activityId
148 await actorFollow.save({ transaction })
149 }
150}
151
152async function isFollowingInstance (targetActor: MActorId) {
153 const serverActor = await getServerActor()
154
155 return targetActor.id === serverActor.id
156}