fix: add id for activitypub follows (#8689)
* add id for activitypub follows * fix lint * fix: follower must be local, followee must be remote Misskey will only use ActivityPub follow requests for users that are local and are requesting to follow a remote user. This check is to ensure that this endpoint can not be used by other services or instances. * fix: missing import * render block with id * fix comment
This commit is contained in:
		
							parent
							
								
									9954c054a7
								
							
						
					
					
						commit
						32dff28460
					
				
					 5 changed files with 63 additions and 15 deletions
				
			
		|  | @ -1,8 +1,20 @@ | ||||||
| import config from '@/config/index.js'; | import config from '@/config/index.js'; | ||||||
| import { ILocalUser, IRemoteUser } from '@/models/entities/user.js'; | import { Blocking } from '@/models/entities/blocking.js'; | ||||||
| 
 | 
 | ||||||
| export default (blocker: ILocalUser, blockee: IRemoteUser) => ({ | /** | ||||||
| 	type: 'Block', |  * Renders a block into its ActivityPub representation. | ||||||
| 	actor: `${config.url}/users/${blocker.id}`, |  * | ||||||
| 	object: blockee.uri, |  * @param block The block to be rendered. The blockee relation must be loaded. | ||||||
| }); |  */ | ||||||
|  | export function renderBlock(block: Blocking) { | ||||||
|  | 	if (block.blockee?.url == null) { | ||||||
|  | 		throw new Error('renderBlock: missing blockee uri'); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return { | ||||||
|  | 		type: 'Block', | ||||||
|  | 		id: `${config.url}/blocks/${block.id}`, | ||||||
|  | 		actor: `${config.url}/users/${block.blockerId}`, | ||||||
|  | 		object: block.blockee.uri, | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -4,12 +4,11 @@ import { Users } from '@/models/index.js'; | ||||||
| 
 | 
 | ||||||
| export default (follower: { id: User['id']; host: User['host']; uri: User['host'] }, followee: { id: User['id']; host: User['host']; uri: User['host'] }, requestId?: string) => { | export default (follower: { id: User['id']; host: User['host']; uri: User['host'] }, followee: { id: User['id']; host: User['host']; uri: User['host'] }, requestId?: string) => { | ||||||
| 	const follow = { | 	const follow = { | ||||||
|  | 		id: requestId ?? `${config.url}/follows/${follower.id}/${followee.id}`, | ||||||
| 		type: 'Follow', | 		type: 'Follow', | ||||||
| 		actor: Users.isLocalUser(follower) ? `${config.url}/users/${follower.id}` : follower.uri, | 		actor: Users.isLocalUser(follower) ? `${config.url}/users/${follower.id}` : follower.uri, | ||||||
| 		object: Users.isLocalUser(followee) ? `${config.url}/users/${followee.id}` : followee.uri, | 		object: Users.isLocalUser(followee) ? `${config.url}/users/${followee.id}` : followee.uri, | ||||||
| 	} as any; | 	} as any; | ||||||
| 
 | 
 | ||||||
| 	if (requestId) follow.id = requestId; |  | ||||||
| 
 |  | ||||||
| 	return follow; | 	return follow; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -15,9 +15,10 @@ import { inbox as processInbox } from '@/queue/index.js'; | ||||||
| import { isSelfHost } from '@/misc/convert-host.js'; | import { isSelfHost } from '@/misc/convert-host.js'; | ||||||
| import { Notes, Users, Emojis, NoteReactions } from '@/models/index.js'; | import { Notes, Users, Emojis, NoteReactions } from '@/models/index.js'; | ||||||
| import { ILocalUser, User } from '@/models/entities/user.js'; | import { ILocalUser, User } from '@/models/entities/user.js'; | ||||||
| import { In, IsNull } from 'typeorm'; | import { In, IsNull, Not } from 'typeorm'; | ||||||
| import { renderLike } from '@/remote/activitypub/renderer/like.js'; | import { renderLike } from '@/remote/activitypub/renderer/like.js'; | ||||||
| import { getUserKeypair } from '@/misc/keypair-store.js'; | import { getUserKeypair } from '@/misc/keypair-store.js'; | ||||||
|  | import renderFollow from '@/remote/activitypub/renderer/follow.js'; | ||||||
| 
 | 
 | ||||||
| // Init router
 | // Init router
 | ||||||
| const router = new Router(); | const router = new Router(); | ||||||
|  | @ -224,4 +225,30 @@ router.get('/likes/:like', async ctx => { | ||||||
| 	setResponseType(ctx); | 	setResponseType(ctx); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | // follow
 | ||||||
|  | router.get('/follows/:follower/:followee', async ctx => { | ||||||
|  | 	// This may be used before the follow is completed, so we do not
 | ||||||
|  | 	// check if the following exists.
 | ||||||
|  | 
 | ||||||
|  | 	const [follower, followee] = await Promise.all([ | ||||||
|  | 		Users.findOneBy({ | ||||||
|  | 			id: ctx.params.follower, | ||||||
|  | 			host: IsNull(), | ||||||
|  | 		}), | ||||||
|  | 		Users.findOneBy({ | ||||||
|  | 			id: ctx.params.followee, | ||||||
|  | 			host: Not(IsNull()), | ||||||
|  | 		}), | ||||||
|  | 	]); | ||||||
|  | 
 | ||||||
|  | 	if (follower == null || followee == null) { | ||||||
|  | 		ctx.status = 404; | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ctx.body = renderActivity(renderFollow(follower, followee)); | ||||||
|  | 	ctx.set('Cache-Control', 'public, max-age=180'); | ||||||
|  | 	setResponseType(ctx); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| export default router; | export default router; | ||||||
|  |  | ||||||
|  | @ -2,9 +2,10 @@ import { publishMainStream, publishUserEvent } from '@/services/stream.js'; | ||||||
| import { renderActivity } from '@/remote/activitypub/renderer/index.js'; | import { renderActivity } from '@/remote/activitypub/renderer/index.js'; | ||||||
| import renderFollow from '@/remote/activitypub/renderer/follow.js'; | import renderFollow from '@/remote/activitypub/renderer/follow.js'; | ||||||
| import renderUndo from '@/remote/activitypub/renderer/undo.js'; | import renderUndo from '@/remote/activitypub/renderer/undo.js'; | ||||||
| import renderBlock from '@/remote/activitypub/renderer/block.js'; | import { renderBlock } from '@/remote/activitypub/renderer/block.js'; | ||||||
| import { deliver } from '@/queue/index.js'; | import { deliver } from '@/queue/index.js'; | ||||||
| import renderReject from '@/remote/activitypub/renderer/reject.js'; | import renderReject from '@/remote/activitypub/renderer/reject.js'; | ||||||
|  | import { Blocking } from '@/models/entities/blocking.js'; | ||||||
| import { User } from '@/models/entities/user.js'; | import { User } from '@/models/entities/user.js'; | ||||||
| import { Blockings, Users, FollowRequests, Followings, UserListJoinings, UserLists } from '@/models/index.js'; | import { Blockings, Users, FollowRequests, Followings, UserListJoinings, UserLists } from '@/models/index.js'; | ||||||
| import { perUserFollowingChart } from '@/services/chart/index.js'; | import { perUserFollowingChart } from '@/services/chart/index.js'; | ||||||
|  | @ -22,15 +23,19 @@ export default async function(blocker: User, blockee: User) { | ||||||
| 		removeFromList(blockee, blocker), | 		removeFromList(blockee, blocker), | ||||||
| 	]); | 	]); | ||||||
| 
 | 
 | ||||||
| 	await Blockings.insert({ | 	const blocking = { | ||||||
| 		id: genId(), | 		id: genId(), | ||||||
| 		createdAt: new Date(), | 		createdAt: new Date(), | ||||||
|  | 		blocker, | ||||||
| 		blockerId: blocker.id, | 		blockerId: blocker.id, | ||||||
|  | 		blockee, | ||||||
| 		blockeeId: blockee.id, | 		blockeeId: blockee.id, | ||||||
| 	}); | 	} as Blocking; | ||||||
|  | 
 | ||||||
|  | 	await Blockings.insert(blocking); | ||||||
| 
 | 
 | ||||||
| 	if (Users.isLocalUser(blocker) && Users.isRemoteUser(blockee)) { | 	if (Users.isLocalUser(blocker) && Users.isRemoteUser(blockee)) { | ||||||
| 		const content = renderActivity(renderBlock(blocker, blockee)); | 		const content = renderActivity(renderBlock(blocking)); | ||||||
| 		deliver(blocker, content, blockee.inbox); | 		deliver(blocker, content, blockee.inbox); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import { renderActivity } from '@/remote/activitypub/renderer/index.js'; | import { renderActivity } from '@/remote/activitypub/renderer/index.js'; | ||||||
| import renderBlock from '@/remote/activitypub/renderer/block.js'; | import { renderBlock } from '@/remote/activitypub/renderer/block.js'; | ||||||
| import renderUndo from '@/remote/activitypub/renderer/undo.js'; | import renderUndo from '@/remote/activitypub/renderer/undo.js'; | ||||||
| import { deliver } from '@/queue/index.js'; | import { deliver } from '@/queue/index.js'; | ||||||
| import Logger from '../logger.js'; | import Logger from '../logger.js'; | ||||||
|  | @ -19,11 +19,16 @@ export default async function(blocker: CacheableUser, blockee: CacheableUser) { | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Since we already have the blocker and blockee, we do not need to fetch
 | ||||||
|  | 	// them in the query above and can just manually insert them here.
 | ||||||
|  | 	blocking.blocker = blocker; | ||||||
|  | 	blocking.blockee = blockee; | ||||||
|  | 
 | ||||||
| 	Blockings.delete(blocking.id); | 	Blockings.delete(blocking.id); | ||||||
| 
 | 
 | ||||||
| 	// deliver if remote bloking
 | 	// deliver if remote bloking
 | ||||||
| 	if (Users.isLocalUser(blocker) && Users.isRemoteUser(blockee)) { | 	if (Users.isLocalUser(blocker) && Users.isRemoteUser(blockee)) { | ||||||
| 		const content = renderActivity(renderUndo(renderBlock(blocker, blockee), blocker)); | 		const content = renderActivity(renderUndo(renderBlock(blocking), blocker)); | ||||||
| 		deliver(blocker, content, blockee.inbox); | 		deliver(blocker, content, blockee.inbox); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue