From ed9f2f49005450655b5caf9f745420fe0154e4e4 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 20 May 2023 00:22:38 +0200 Subject: [PATCH] server: refactor ffVisibility checks into function --- .../backend/src/models/repositories/user.ts | 42 +++++++++++++++---- .../src/server/activitypub/followers.ts | 13 ++---- .../src/server/activitypub/following.ts | 13 ++---- .../server/api/endpoints/users/followers.ts | 23 ++-------- .../server/api/endpoints/users/following.ts | 23 ++-------- 5 files changed, 46 insertions(+), 68 deletions(-) diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 10969a034..4387ed922 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -230,6 +230,34 @@ export const UserRepository = db.getRepository(User).extend({ return `${config.url}/identicon/${userId}`; }, + /** + * Determines whether the followers/following of user `user` are visibile to user `me`. + */ + async areFollowersVisibleTo(user: User, me: { id: User['id'] } | null | undefined): Promise { + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + + switch (profile.ffVisibility) { + case 'public': + return true; + case 'followers': + if (me == null) { + return false; + } else if (me.id === user.id) { + return true; + } else { + return await Followings.count({ + where: { + followerId: me.id, + followeeId: user.id, + }, + take: 1, + }).then(n => n > 0); + } + case 'private': + return me?.id === user.id; + } + } + async pack( src: User['id'] | User, me?: { id: User['id'] } | null | undefined, @@ -270,15 +298,13 @@ export const UserRepository = db.getRepository(User).extend({ .getMany() : []; const profile = opts.detail ? await UserProfiles.findOneByOrFail({ userId: user.id }) : null; - const followingCount = profile == null ? null : - (profile.ffVisibility === 'public') || isMe ? user.followingCount : - (profile.ffVisibility === 'followers') && relation?.isFollowing ? user.followingCount : - null; + const ffVisible = await this.areFollowersVisibleTo(user, me); - const followersCount = profile == null ? null : - (profile.ffVisibility === 'public') || isMe ? user.followersCount : - (profile.ffVisibility === 'followers') && relation?.isFollowing ? user.followersCount : - null; + const followingCount = opts.detail ? null : + ffVisible ? user.followingCount : null; + + const followersCount = opts.detail ? null : + ffVisible ? user.followersCount : null; const packed = { id: user.id, diff --git a/packages/backend/src/server/activitypub/followers.ts b/packages/backend/src/server/activitypub/followers.ts index beb48713a..2c2b6cfb4 100644 --- a/packages/backend/src/server/activitypub/followers.ts +++ b/packages/backend/src/server/activitypub/followers.ts @@ -6,7 +6,7 @@ import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-collection.js'; import renderOrderedCollectionPage from '@/remote/activitypub/renderer/ordered-collection-page.js'; import renderFollowUser from '@/remote/activitypub/renderer/follow-user.js'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; +import { Users, Followings } from '@/models/index.js'; import { Following } from '@/models/entities/following.js'; import { setResponseType } from '../activitypub.js'; @@ -31,19 +31,12 @@ export default async (ctx: Router.RouterContext) => { return; } - //#region Check ff visibility - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - if (profile.ffVisibility === 'private') { - ctx.status = 403; - ctx.set('Cache-Control', 'public, max-age=30'); - return; - } else if (profile.ffVisibility === 'followers') { + const ffVisible = await Users.areFollowersVisibleTo(user, null); + if (!ffVisible) { ctx.status = 403; ctx.set('Cache-Control', 'public, max-age=30'); return; } - //#endregion const limit = 10; const partOf = `${config.url}/users/${userId}/followers`; diff --git a/packages/backend/src/server/activitypub/following.ts b/packages/backend/src/server/activitypub/following.ts index 3a25a6316..4e156a19f 100644 --- a/packages/backend/src/server/activitypub/following.ts +++ b/packages/backend/src/server/activitypub/following.ts @@ -6,7 +6,7 @@ import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-collection.js'; import renderOrderedCollectionPage from '@/remote/activitypub/renderer/ordered-collection-page.js'; import renderFollowUser from '@/remote/activitypub/renderer/follow-user.js'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; +import { Users, Followings } from '@/models/index.js'; import { Following } from '@/models/entities/following.js'; import { setResponseType } from '../activitypub.js'; @@ -31,19 +31,12 @@ export default async (ctx: Router.RouterContext) => { return; } - //#region Check ff visibility - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - if (profile.ffVisibility === 'private') { - ctx.status = 403; - ctx.set('Cache-Control', 'public, max-age=30'); - return; - } else if (profile.ffVisibility === 'followers') { + const ffVisible = await Users.areFollowersVisibleTo(user, null); + if (!ffVisible) { ctx.status = 403; ctx.set('Cache-Control', 'public, max-age=30'); return; } - //#endregion const limit = 10; const partOf = `${config.url}/users/${userId}/following`; diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 2595fbff5..e93851cd8 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,5 +1,5 @@ import { IsNull } from 'typeorm'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; +import { Users, Followings } from '@/models/index.js'; import { toPunyNullable } from '@/misc/convert-host.js'; import define from '@/server/api/define.js'; import { ApiError } from '@/server/api/error.js'; @@ -61,25 +61,8 @@ export default define(meta, paramDef, async (ps, me) => { if (user == null) throw new ApiError('NO_SUCH_USER'); - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - if (profile.ffVisibility === 'private') { - if (me == null || (me.id !== user.id)) { - throw new ApiError('ACCESS_DENIED'); - } - } else if (profile.ffVisibility === 'followers') { - if (me == null) { - throw new ApiError('ACCESS_DENIED'); - } else if (me.id !== user.id) { - const following = await Followings.countBy({ - followeeId: user.id, - followerId: me.id, - }); - if (!following) { - throw new ApiError('ACCESS_DENIED'); - } - } - } + const ffVisible = await Users.areFollowersVisibleTo(user, me); + if (!ffVisible) throw new ApiError('ACCESS_DENIED'); const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) .andWhere('following.followeeId = :userId', { userId: user.id }) diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 0bf60c079..406853423 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,5 +1,5 @@ import { IsNull } from 'typeorm'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; +import { Users, Followings } from '@/models/index.js'; import { toPunyNullable } from '@/misc/convert-host.js'; import define from '@/server/api/define.js'; import { ApiError } from '@/server/api/error.js'; @@ -61,25 +61,8 @@ export default define(meta, paramDef, async (ps, me) => { if (user == null) throw new ApiError('NO_SUCH_USER'); - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - if (profile.ffVisibility === 'private') { - if (me == null || (me.id !== user.id)) { - throw new ApiError('ACCESS_DENIED'); - } - } else if (profile.ffVisibility === 'followers') { - if (me == null) { - throw new ApiError('ACCESS_DENIED'); - } else if (me.id !== user.id) { - const following = await Followings.countBy({ - followeeId: user.id, - followerId: me.id, - }); - if (!following) { - throw new ApiError('ACCESS_DENIED'); - } - } - } + const ffVisible = await Users.areFollowersVisibleTo(user, me); + if (!ffVisible) throw new ApiError('ACCESS_DENIED'); const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) .andWhere('following.followerId = :userId', { userId: user.id })