From 8bd41f5c9ee8e39a687033ea4c4e2fba9ca5db90 Mon Sep 17 00:00:00 2001 From: nullobsi Date: Mon, 19 Jul 2021 15:41:23 -0700 Subject: [PATCH 1/5] Add migration for allowedHosts, secureMode, privateMode --- .../1626733991004-allowlist-secure-mode.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 packages/backend/migration/1626733991004-allowlist-secure-mode.js diff --git a/packages/backend/migration/1626733991004-allowlist-secure-mode.js b/packages/backend/migration/1626733991004-allowlist-secure-mode.js new file mode 100644 index 000000000..bfd859671 --- /dev/null +++ b/packages/backend/migration/1626733991004-allowlist-secure-mode.js @@ -0,0 +1,15 @@ +export class allowlistSecureMode1626733991004 { + name = 'allowlistSecureMode1626733991004'; + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "allowedHosts" character varying(256) [] default '{}'`); + await queryRunner.query(`ALTER TABLE "meta" ADD "secureMode" bool default false`); + await queryRunner.query(`ALTER TABLE "meta" ADD "privateMode" bool default false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "allowedHosts"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "secureMode"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "privateMode"`); + } +} + -- 2.34.1 From 9acd4bc85535724d04e0b6a7222778672312aa29 Mon Sep 17 00:00:00 2001 From: nullobsi Date: Tue, 20 Jul 2021 09:45:41 -0700 Subject: [PATCH 2/5] Add Secure Mode and Private Mode - Add instance actor - Add private mode, which uses an allowlist - Add Secure Mode, restricts access to blocked instances Co-authored-by: Francis Dinh --- packages/backend/src/models/entities/meta.ts | 15 +++ .../backend/src/queue/processors/deliver.ts | 4 + .../backend/src/queue/processors/inbox.ts | 5 + .../src/remote/activitypub/check-fetch.ts | 70 ++++++++++ .../src/remote/activitypub/resolver.ts | 6 +- packages/backend/src/server/activitypub.ts | 121 ++++++++++++++++-- .../src/server/activitypub/featured.ts | 17 ++- .../src/server/activitypub/followers.ts | 15 ++- .../src/server/activitypub/following.ts | 15 ++- .../backend/src/server/activitypub/outbox.ts | 16 ++- packages/backend/src/server/api/endpoints.ts | 6 + .../backend/src/server/api/endpoints/meta.ts | 10 ++ 12 files changed, 287 insertions(+), 13 deletions(-) create mode 100644 packages/backend/src/remote/activitypub/check-fetch.ts diff --git a/packages/backend/src/models/entities/meta.ts b/packages/backend/src/models/entities/meta.ts index 0afb7c7fc..e026f04f7 100644 --- a/packages/backend/src/models/entities/meta.ts +++ b/packages/backend/src/models/entities/meta.ts @@ -77,6 +77,21 @@ export class Meta { }) public blockedHosts: string[]; + @Column('boolean', { + default: false + }) + public secureMode: boolean; + + @Column('boolean', { + default: false + }) + public privateMode: boolean; + + @Column('varchar', { + length: 256, array: true, default: '{}' + }) + public allowedHosts: string[]; + @Column('varchar', { length: 512, array: true, default: '{/featured,/channels,/explore,/pages,/about-foundkey}', }) diff --git a/packages/backend/src/queue/processors/deliver.ts b/packages/backend/src/queue/processors/deliver.ts index 108379aa8..b937b18d5 100644 --- a/packages/backend/src/queue/processors/deliver.ts +++ b/packages/backend/src/queue/processors/deliver.ts @@ -28,6 +28,10 @@ export default async (job: Bull.Job) => { return 'skip (blocked)'; } + if (meta.privateMode && !meta.allowedHosts.includes(toPuny(host))) { + return 'skip (not allowed)'; + } + // isSuspendedなら中断 let suspendedHosts = suspendedHostsCache.get(null); if (suspendedHosts == null) { diff --git a/packages/backend/src/queue/processors/inbox.ts b/packages/backend/src/queue/processors/inbox.ts index 426713358..44b9e3f4e 100644 --- a/packages/backend/src/queue/processors/inbox.ts +++ b/packages/backend/src/queue/processors/inbox.ts @@ -39,6 +39,11 @@ export default async (job: Bull.Job): Promise => { return `Blocked request: ${host}`; } + // Only permitted instances if in private mode. + if (meta.privateMode && !meta.allowedHosts.includes(host)) { + return `Blocked request: ${host}`; + } + const keyIdLower = signature.keyId.toLowerCase(); if (keyIdLower.startsWith('acct:')) { return `Old keyId is no longer supported. ${keyIdLower}`; diff --git a/packages/backend/src/remote/activitypub/check-fetch.ts b/packages/backend/src/remote/activitypub/check-fetch.ts new file mode 100644 index 000000000..63103aa64 --- /dev/null +++ b/packages/backend/src/remote/activitypub/check-fetch.ts @@ -0,0 +1,70 @@ +import config from '@/config/index.js'; +import { IncomingMessage } from 'http'; +import { fetchMeta } from '@/misc/fetch-meta.js'; +import httpSignature from '@peertube/http-signature'; +import { URL } from 'url'; +import { toPuny } from '@/misc/convert-host.js'; +import DbResolver from '@/remote/activitypub/db-resolver.js'; +import { getApId } from '@/remote/activitypub/type.js'; + + +export default async function checkFetch(req: IncomingMessage): Promise { + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + let signature; + + try { + signature = httpSignature.parseRequest(req, { 'headers': [] }); + } catch (e) { + return 401; + } + + const keyId = new URL(signature.keyId); + const host = toPuny(keyId.hostname); + + if (meta.blockedHosts.includes(host)) { + return 403; + } + + if (meta.privateMode && host !== config.host && !meta.allowedHosts.includes(host)) { + return 403; + } + + const keyIdLower = signature.keyId.toLowerCase(); + if (keyIdLower.startsWith('acct:')) { + // Old keyId is no longer supported. + return 401; + } + + const dbResolver = new DbResolver(); + + // Get user from database based on HTTP-Signature keyId + let authUser = await dbResolver.getAuthUserFromKeyId(signature.keyId); + + // If keyid is unknown, try resolving it + if (authUser == null) { + try { + keyId.hash = ''; + authUser = await dbResolver.getAuthUserFromApId(getApId(keyId.toString())); + } catch (e) { + return 403; + } + } + + if (authUser?.key == null) { + return 403; + } + + if (authUser.user.host !== host) { + return 403; + } + + // HTTP-Signature validation + const httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem); + + if (!httpSignatureValidated) { + return 403; + } + } + return 200; +} diff --git a/packages/backend/src/remote/activitypub/resolver.ts b/packages/backend/src/remote/activitypub/resolver.ts index 3cea4c44e..26cf4c6df 100644 --- a/packages/backend/src/remote/activitypub/resolver.ts +++ b/packages/backend/src/remote/activitypub/resolver.ts @@ -72,7 +72,11 @@ export default class Resolver { throw new Error('Instance is blocked'); } - if (!this.user) { + if (meta.privateMode && config.host !== host && !meta.allowedHosts.includes(host)) { + throw new Error('Instance is not allowed'); + } + + if (config.signToActivityPubGet && !this.user) { this.user = await getInstanceActor(); } diff --git a/packages/backend/src/server/activitypub.ts b/packages/backend/src/server/activitypub.ts index f1a8f4914..dd66f29c4 100644 --- a/packages/backend/src/server/activitypub.ts +++ b/packages/backend/src/server/activitypub.ts @@ -9,11 +9,14 @@ import renderKey from '@/remote/activitypub/renderer/key.js'; import { renderPerson } from '@/remote/activitypub/renderer/person.js'; import renderEmoji from '@/remote/activitypub/renderer/emoji.js'; import { inbox as processInbox } from '@/queue/index.js'; -import { isSelfHost } from '@/misc/convert-host.js'; +import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import { Notes, Users, Emojis, NoteReactions } from '@/models/index.js'; import { ILocalUser, User } from '@/models/entities/user.js'; import { renderLike } from '@/remote/activitypub/renderer/like.js'; import { getUserKeypair } from '@/misc/keypair-store.js'; +import checkFetch from '@/remote/activitypub/check-fetch.js'; +import { getInstanceActor } from '@/services/instance-actor.js'; +import { fetchMeta } from '@/misc/fetch-meta.js'; import renderFollow from '@/remote/activitypub/renderer/follow.js'; import Outbox, { packActivity } from './activitypub/outbox.js'; import Followers from './activitypub/followers.js'; @@ -66,6 +69,12 @@ router.post('/users/:user/inbox', json(), inbox); router.get('/notes/:note', async (ctx, next) => { if (!isActivityPubReq(ctx)) return await next(); + const verify = await checkFetch(ctx.req); + if (verify != 200) { + ctx.status = verify; + return; + } + const note = await Notes.findOneBy({ id: ctx.params.note, visibility: In(['public' as const, 'home' as const]), @@ -88,12 +97,24 @@ router.get('/notes/:note', async (ctx, next) => { } ctx.body = renderActivity(await renderNote(note, false)); - ctx.set('Cache-Control', 'public, max-age=180'); + + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + ctx.set('Cache-Control', 'no-store'); + } else { + ctx.set('Cache-Control', 'public, max-age=180'); + } setResponseType(ctx); }); // note activity router.get('/notes/:note/activity', async ctx => { + const verify = await checkFetch(ctx.req); + if (verify != 200) { + ctx.status = verify; + return; + } + const note = await Notes.findOneBy({ id: ctx.params.note, userHost: IsNull(), @@ -107,7 +128,12 @@ router.get('/notes/:note/activity', async ctx => { } ctx.body = renderActivity(await packActivity(note)); - ctx.set('Cache-Control', 'public, max-age=180'); + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + ctx.set('Cache-Control', 'no-store'); + } else { + ctx.set('Cache-Control', 'public, max-age=180'); + } setResponseType(ctx); }); @@ -125,6 +151,20 @@ router.get('/users/:user/collections/featured', Featured); // publickey router.get('/users/:user/publickey', async ctx => { + const instanceActor = await getInstanceActor(); + if (ctx.params.user === instanceActor.id) { + ctx.body = renderActivity(renderKey(instanceActor, await getUserKeypair(instanceActor.id))); + ctx.set('Cache-Control', 'public, max-age=180'); + setResponseType(ctx); + return; + } + + const verify = await checkFetch(ctx.req); + if (verify != 200) { + ctx.status = verify; + return; + } + const userId = ctx.params.user; const user = await Users.findOneBy({ @@ -141,7 +181,12 @@ router.get('/users/:user/publickey', async ctx => { if (Users.isLocalUser(user)) { ctx.body = renderActivity(renderKey(user, keypair)); - ctx.set('Cache-Control', 'public, max-age=180'); + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + ctx.set('Cache-Control', 'no-store'); + } else { + ctx.set('Cache-Control', 'public, max-age=180'); + } setResponseType(ctx); } else { ctx.status = 400; @@ -156,13 +201,30 @@ async function userInfo(ctx: Router.RouterContext, user: User | null) { } ctx.body = renderActivity(await renderPerson(user as ILocalUser)); - ctx.set('Cache-Control', 'public, max-age=180'); + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + ctx.set('Cache-Control', 'no-store'); + } else { + ctx.set('Cache-Control', 'public, max-age=180'); + } setResponseType(ctx); } router.get('/users/:user', async (ctx, next) => { if (!isActivityPubReq(ctx)) return await next(); + const instanceActor = await getInstanceActor(); + if (ctx.params.user === instanceActor.id) { + await userInfo(ctx, instanceActor); + return; + } + + const verify = await checkFetch(ctx.req); + if (verify != 200) { + ctx.status = verify; + return; + } + const userId = ctx.params.user; const user = await Users.findOneBy({ @@ -177,6 +239,18 @@ router.get('/users/:user', async (ctx, next) => { router.get('/@:user', async (ctx, next) => { if (!isActivityPubReq(ctx)) return await next(); + if (ctx.params.user === 'instance.actor') { + const instanceActor = await getInstanceActor(); + await userInfo(ctx, instanceActor); + return; + } + + const verify = await checkFetch(ctx.req); + if (verify != 200) { + ctx.status = verify; + return; + } + const user = await Users.findOneBy({ usernameLower: ctx.params.user.toLowerCase(), host: IsNull(), @@ -185,6 +259,11 @@ router.get('/@:user', async (ctx, next) => { await userInfo(ctx, user); }); + +router.get('/actor', async (ctx, next) => { + const instanceActor = await getInstanceActor(); + await userInfo(ctx, instanceActor); +}); //#endregion // emoji @@ -200,12 +279,23 @@ router.get('/emojis/:emoji', async ctx => { } ctx.body = renderActivity(await renderEmoji(emoji)); - ctx.set('Cache-Control', 'public, max-age=180'); + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + ctx.set('Cache-Control', 'no-store'); + } else { + ctx.set('Cache-Control', 'public, max-age=180'); + } setResponseType(ctx); }); // like router.get('/likes/:like', async ctx => { + const verify = await checkFetch(ctx.req); + if (verify != 200) { + ctx.status = verify; + return; + } + const note = await Notes.findOneBy({ id: reaction.noteId, visibility: In(['public' as const, 'home' as const]), @@ -224,12 +314,22 @@ router.get('/likes/:like', async ctx => { } ctx.body = renderActivity(await renderLike(reaction, note)); - ctx.set('Cache-Control', 'public, max-age=180'); + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + ctx.set('Cache-Control', 'no-store'); + } else { + ctx.set('Cache-Control', 'public, max-age=180'); + } setResponseType(ctx); }); // follow router.get('/follows/:follower/:followee', async ctx => { + const verify = await checkFetch(ctx.req); + if (verify != 200) { + ctx.status = verify; + return; + } // This may be used before the follow is completed, so we do not // check if the following exists. @@ -250,7 +350,12 @@ router.get('/follows/:follower/:followee', async ctx => { } ctx.body = renderActivity(renderFollow(follower, followee)); - ctx.set('Cache-Control', 'public, max-age=180'); + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + ctx.set('Cache-Control', 'no-store'); + } else { + ctx.set('Cache-Control', 'public, max-age=180'); + } setResponseType(ctx); }); diff --git a/packages/backend/src/server/activitypub/featured.ts b/packages/backend/src/server/activitypub/featured.ts index 09906250f..6d3680798 100644 --- a/packages/backend/src/server/activitypub/featured.ts +++ b/packages/backend/src/server/activitypub/featured.ts @@ -6,8 +6,17 @@ import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-colle import renderNote from '@/remote/activitypub/renderer/note.js'; import { Users, Notes, UserNotePinings } from '@/models/index.js'; import { setResponseType } from '../activitypub.js'; +import { IsNull } from 'typeorm'; +import checkFetch from '@/remote/activitypub/check-fetch.js'; +import { fetchMeta } from '@/misc/fetch-meta.js'; export default async (ctx: Router.RouterContext) => { + const verify = await checkFetch(ctx.req); + if (verify != 200) { + ctx.status = verify; + return; + } + const userId = ctx.params.user; const user = await Users.findOneBy({ @@ -36,6 +45,12 @@ export default async (ctx: Router.RouterContext) => { ); ctx.body = renderActivity(rendered); - ctx.set('Cache-Control', 'public, max-age=180'); + + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + ctx.set('Cache-Control', 'no-store'); + } else { + ctx.set('Cache-Control', 'public, max-age=180'); + } setResponseType(ctx); }; diff --git a/packages/backend/src/server/activitypub/followers.ts b/packages/backend/src/server/activitypub/followers.ts index beb48713a..3c8ea9458 100644 --- a/packages/backend/src/server/activitypub/followers.ts +++ b/packages/backend/src/server/activitypub/followers.ts @@ -9,8 +9,16 @@ import renderFollowUser from '@/remote/activitypub/renderer/follow-user.js'; import { Users, Followings, UserProfiles } from '@/models/index.js'; import { Following } from '@/models/entities/following.js'; import { setResponseType } from '../activitypub.js'; +import checkFetch from '@/remote/activitypub/check-fetch.js'; +import { fetchMeta } from '@/misc/fetch-meta.js'; export default async (ctx: Router.RouterContext) => { + const verify = await checkFetch(ctx.req); + if (verify != 200) { + ctx.status = verify; + return; + } + const userId = ctx.params.user; const cursor = ctx.request.query.cursor; @@ -89,7 +97,12 @@ export default async (ctx: Router.RouterContext) => { // index page const rendered = renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`); ctx.body = renderActivity(rendered); - ctx.set('Cache-Control', 'public, max-age=180'); setResponseType(ctx); } + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + ctx.set('Cache-Control', 'no-store'); + } else { + ctx.set('Cache-Control', 'public, max-age=180'); + } }; diff --git a/packages/backend/src/server/activitypub/following.ts b/packages/backend/src/server/activitypub/following.ts index 3a25a6316..836cd4d26 100644 --- a/packages/backend/src/server/activitypub/following.ts +++ b/packages/backend/src/server/activitypub/following.ts @@ -9,8 +9,16 @@ import renderFollowUser from '@/remote/activitypub/renderer/follow-user.js'; import { Users, Followings, UserProfiles } from '@/models/index.js'; import { Following } from '@/models/entities/following.js'; import { setResponseType } from '../activitypub.js'; +import checkFetch from '@/remote/activitypub/check-fetch.js'; +import { fetchMeta } from '@/misc/fetch-meta.js'; export default async (ctx: Router.RouterContext) => { + const verify = await checkFetch(ctx.req); + if (verify != 200) { + ctx.status = verify; + return; + } + const userId = ctx.params.user; const cursor = ctx.request.query.cursor; @@ -89,7 +97,12 @@ export default async (ctx: Router.RouterContext) => { // index page const rendered = renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`); ctx.body = renderActivity(rendered); - ctx.set('Cache-Control', 'public, max-age=180'); setResponseType(ctx); } + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + ctx.set('Cache-Control', 'no-store'); + } else { + ctx.set('Cache-Control', 'public, max-age=180'); + } }; diff --git a/packages/backend/src/server/activitypub/outbox.ts b/packages/backend/src/server/activitypub/outbox.ts index d88b6c899..5dfaff366 100644 --- a/packages/backend/src/server/activitypub/outbox.ts +++ b/packages/backend/src/server/activitypub/outbox.ts @@ -14,8 +14,16 @@ import { Note } from '@/models/entities/note.js'; import { isPureRenote } from '@/misc/renote.js'; import { makePaginationQuery } from '../api/common/make-pagination-query.js'; import { setResponseType } from '../activitypub.js'; +import checkFetch from '@/remote/activitypub/check-fetch.js'; +import { fetchMeta } from '@/misc/fetch-meta.js'; export default async (ctx: Router.RouterContext) => { + const verify = await checkFetch(ctx.req); + if (verify != 200) { + ctx.status = verify; + return; + } + const userId = ctx.params.user; const sinceId = ctx.request.query.since_id; @@ -90,9 +98,15 @@ export default async (ctx: Router.RouterContext) => { `${partOf}?page=true&since_id=000000000000000000000000`, ); ctx.body = renderActivity(rendered); - ctx.set('Cache-Control', 'public, max-age=180'); + setResponseType(ctx); } + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + ctx.set('Cache-Control', 'no-store'); + } else { + ctx.set('Cache-Control', 'public, max-age=180'); + } }; /** diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index e7563555b..c9052ed9a 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -692,6 +692,12 @@ export interface IEndpointMeta { */ readonly secure?: boolean; + /** + * If in private mode, whether credentials are required when making a request to this endpoint. + * If omitted, this is interpreted as false. + */ + readonly requireCredentialPrivateMode?: boolean; + /** * エンドポイントの種類 * パーミッションの実現に利用されます。 diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index cbf74ef04..205d0916f 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -249,6 +249,16 @@ export const meta = { }, }, }, + secureMode: { + type: 'boolean', + optional: true, nullable: false, + default: false, + }, + privateMode: { + type: 'boolean', + optional: true, nullable: false, + default: false, + }, }, }, } as const; -- 2.34.1 From 840227a9012436e3ebc4a870b0a920a05b1a9deb Mon Sep 17 00:00:00 2001 From: nullobsi Date: Tue, 20 Jul 2021 11:51:59 -0700 Subject: [PATCH 3/5] In private mode, block access to many public APIs --- packages/backend/src/server/api/call.ts | 13 ++++++++ .../src/server/api/endpoints/announcements.ts | 1 + .../server/api/endpoints/channels/featured.ts | 1 + .../src/server/api/endpoints/channels/show.ts | 1 + .../server/api/endpoints/channels/timeline.ts | 1 + .../api/endpoints/charts/active-users.ts | 1 + .../server/api/endpoints/charts/ap-request.ts | 1 + .../src/server/api/endpoints/charts/drive.ts | 1 + .../server/api/endpoints/charts/federation.ts | 1 + .../server/api/endpoints/charts/hashtag.ts | 1 + .../server/api/endpoints/charts/instance.ts | 1 + .../src/server/api/endpoints/charts/notes.ts | 1 + .../server/api/endpoints/charts/user/drive.ts | 1 + .../api/endpoints/charts/user/following.ts | 1 + .../server/api/endpoints/charts/user/notes.ts | 1 + .../api/endpoints/charts/user/reactions.ts | 1 + .../src/server/api/endpoints/charts/users.ts | 1 + .../src/server/api/endpoints/clips/notes.ts | 1 + .../src/server/api/endpoints/clips/show.ts | 1 + .../api/endpoints/federation/followers.ts | 1 + .../api/endpoints/federation/following.ts | 1 + .../api/endpoints/federation/instances.ts | 1 + .../api/endpoints/federation/show-instance.ts | 1 + .../server/api/endpoints/federation/users.ts | 1 + .../server/api/endpoints/gallery/featured.ts | 1 + .../server/api/endpoints/gallery/popular.ts | 1 + .../src/server/api/endpoints/gallery/posts.ts | 1 + .../api/endpoints/gallery/posts/show.ts | 1 + .../api/endpoints/get-online-users-count.ts | 1 + .../src/server/api/endpoints/hashtags/list.ts | 1 + .../server/api/endpoints/hashtags/search.ts | 1 + .../src/server/api/endpoints/hashtags/show.ts | 1 + .../server/api/endpoints/hashtags/trend.ts | 1 + .../server/api/endpoints/hashtags/users.ts | 1 + .../backend/src/server/api/endpoints/meta.ts | 30 ++++++++++++------- .../backend/src/server/api/endpoints/notes.ts | 1 + .../server/api/endpoints/notes/children.ts | 3 +- .../src/server/api/endpoints/notes/clips.ts | 1 + .../api/endpoints/notes/conversation.ts | 1 + .../server/api/endpoints/notes/featured.ts | 1 + .../api/endpoints/notes/global-timeline.ts | 1 + .../api/endpoints/notes/local-timeline.ts | 1 + .../server/api/endpoints/notes/reactions.ts | 1 + .../src/server/api/endpoints/notes/renotes.ts | 1 + .../src/server/api/endpoints/notes/replies.ts | 1 + .../api/endpoints/notes/search-by-tag.ts | 1 + .../src/server/api/endpoints/notes/search.ts | 1 + .../src/server/api/endpoints/notes/show.ts | 1 + .../server/api/endpoints/notes/translate.ts | 1 + .../server/api/endpoints/pages/featured.ts | 1 + .../src/server/api/endpoints/pages/show.ts | 1 + .../src/server/api/endpoints/pinned-users.ts | 1 + .../src/server/api/endpoints/server-info.ts | 1 + .../backend/src/server/api/endpoints/stats.ts | 1 + .../backend/src/server/api/endpoints/users.ts | 1 + .../src/server/api/endpoints/users/clips.ts | 1 + .../server/api/endpoints/users/followers.ts | 1 + .../server/api/endpoints/users/following.ts | 1 + .../api/endpoints/users/gallery/posts.ts | 1 + .../users/get-frequently-replied-users.ts | 1 + .../src/server/api/endpoints/users/notes.ts | 1 + .../src/server/api/endpoints/users/pages.ts | 1 + .../server/api/endpoints/users/reactions.ts | 1 + .../users/search-by-username-and-host.ts | 1 + .../src/server/api/endpoints/users/search.ts | 1 + .../src/server/api/endpoints/users/show.ts | 1 + .../src/server/api/endpoints/users/stats.ts | 1 + 67 files changed, 99 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/server/api/call.ts b/packages/backend/src/server/api/call.ts index fe1698dbc..29769d602 100644 --- a/packages/backend/src/server/api/call.ts +++ b/packages/backend/src/server/api/call.ts @@ -7,6 +7,8 @@ import { limiter } from './limiter.js'; import endpoints, { IEndpointMeta } from './endpoints.js'; import { ApiError } from './error.js'; import { apiLogger } from './logger.js'; +import { AccessToken } from '@/models/entities/access-token.js'; +import { fetchMeta } from '@/misc/fetch-meta.js'; const accessDenied = { message: 'Access denied.', @@ -93,6 +95,17 @@ export default async (endpoint: string, user: CacheableLocalUser | null | undefi }); } + // private mode + const meta = await fetchMeta(); + if (meta.privateMode && ep.meta.requireCredentialPrivateMode && user == null) { + throw new ApiError({ + message: 'Credential required.', + code: 'CREDENTIAL_REQUIRED', + id: '1384574d-a912-4b81-8601-c7b1c4085df1', + httpStatusCode: 401 + }); + } + // Cast non JSON input if ((ep.meta.requireFile || ctx?.method === 'GET') && ep.params.properties) { for (const k of Object.keys(ep.params.properties)) { diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts index 23cb93c9a..189de042b 100644 --- a/packages/backend/src/server/api/endpoints/announcements.ts +++ b/packages/backend/src/server/api/endpoints/announcements.ts @@ -6,6 +6,7 @@ export const meta = { tags: ['meta'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/channels/featured.ts b/packages/backend/src/server/api/endpoints/channels/featured.ts index 9c8f63e6c..bb51fd447 100644 --- a/packages/backend/src/server/api/endpoints/channels/featured.ts +++ b/packages/backend/src/server/api/endpoints/channels/featured.ts @@ -5,6 +5,7 @@ export const meta = { tags: ['channels'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/channels/show.ts b/packages/backend/src/server/api/endpoints/channels/show.ts index 0e55e73a0..29fe9a00e 100644 --- a/packages/backend/src/server/api/endpoints/channels/show.ts +++ b/packages/backend/src/server/api/endpoints/channels/show.ts @@ -6,6 +6,7 @@ export const meta = { tags: ['channels'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'object', diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 90db2db37..584e9f87e 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -8,6 +8,7 @@ export const meta = { tags: ['notes', 'channels'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/charts/active-users.ts b/packages/backend/src/server/api/endpoints/charts/active-users.ts index ea2379429..216676020 100644 --- a/packages/backend/src/server/api/endpoints/charts/active-users.ts +++ b/packages/backend/src/server/api/endpoints/charts/active-users.ts @@ -4,6 +4,7 @@ import define from '../../define.js'; export const meta = { tags: ['charts', 'users'], + requireCredentialPrivateMode: true, res: getJsonSchema(activeUsersChart.schema), diff --git a/packages/backend/src/server/api/endpoints/charts/ap-request.ts b/packages/backend/src/server/api/endpoints/charts/ap-request.ts index 06dee250e..a8f6e4564 100644 --- a/packages/backend/src/server/api/endpoints/charts/ap-request.ts +++ b/packages/backend/src/server/api/endpoints/charts/ap-request.ts @@ -4,6 +4,7 @@ import define from '../../define.js'; export const meta = { tags: ['charts'], + requireCredentialPrivateMode: true, res: getJsonSchema(apRequestChart.schema), diff --git a/packages/backend/src/server/api/endpoints/charts/drive.ts b/packages/backend/src/server/api/endpoints/charts/drive.ts index dd2c2d683..14f82e39d 100644 --- a/packages/backend/src/server/api/endpoints/charts/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/drive.ts @@ -4,6 +4,7 @@ import define from '../../define.js'; export const meta = { tags: ['charts', 'drive'], + requireCredentialPrivateMode: true, res: getJsonSchema(driveChart.schema), diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index 8c35b3c46..141e005ee 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -4,6 +4,7 @@ import define from '../../define.js'; export const meta = { tags: ['charts'], + requireCredentialPrivateMode: true, res: getJsonSchema(federationChart.schema), diff --git a/packages/backend/src/server/api/endpoints/charts/hashtag.ts b/packages/backend/src/server/api/endpoints/charts/hashtag.ts index 77e24a62c..d34153bc1 100644 --- a/packages/backend/src/server/api/endpoints/charts/hashtag.ts +++ b/packages/backend/src/server/api/endpoints/charts/hashtag.ts @@ -4,6 +4,7 @@ import define from '../../define.js'; export const meta = { tags: ['charts', 'hashtags'], + requireCredentialPrivateMode: true, res: getJsonSchema(hashtagChart.schema), diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index 817d51ad0..3d9619d24 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -4,6 +4,7 @@ import define from '../../define.js'; export const meta = { tags: ['charts'], + requireCredentialPrivateMode: true, res: getJsonSchema(instanceChart.schema), diff --git a/packages/backend/src/server/api/endpoints/charts/notes.ts b/packages/backend/src/server/api/endpoints/charts/notes.ts index 951adf540..42befed27 100644 --- a/packages/backend/src/server/api/endpoints/charts/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/notes.ts @@ -4,6 +4,7 @@ import define from '../../define.js'; export const meta = { tags: ['charts', 'notes'], + requireCredentialPrivateMode: true, res: getJsonSchema(notesChart.schema), diff --git a/packages/backend/src/server/api/endpoints/charts/user/drive.ts b/packages/backend/src/server/api/endpoints/charts/user/drive.ts index f165b4022..cb73b4ac9 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/drive.ts @@ -4,6 +4,7 @@ import define from '../../../define.js'; export const meta = { tags: ['charts', 'drive', 'users'], + requireCredentialPrivateMode: true, res: getJsonSchema(perUserDriveChart.schema), diff --git a/packages/backend/src/server/api/endpoints/charts/user/following.ts b/packages/backend/src/server/api/endpoints/charts/user/following.ts index d7f2334ea..e9bf6f741 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/following.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/following.ts @@ -4,6 +4,7 @@ import define from '../../../define.js'; export const meta = { tags: ['charts', 'users', 'following'], + requireCredentialPrivateMode: true, res: getJsonSchema(perUserFollowingChart.schema), diff --git a/packages/backend/src/server/api/endpoints/charts/user/notes.ts b/packages/backend/src/server/api/endpoints/charts/user/notes.ts index aefe550d4..5b576754d 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/notes.ts @@ -4,6 +4,7 @@ import define from '../../../define.js'; export const meta = { tags: ['charts', 'users', 'notes'], + requireCredentialPrivateMode: true, res: getJsonSchema(perUserNotesChart.schema), diff --git a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts index 6bc6b56bf..61c4527b9 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts @@ -4,6 +4,7 @@ import define from '../../../define.js'; export const meta = { tags: ['charts', 'users', 'reactions'], + requireCredentialPrivateMode: true, res: getJsonSchema(perUserReactionsChart.schema), diff --git a/packages/backend/src/server/api/endpoints/charts/users.ts b/packages/backend/src/server/api/endpoints/charts/users.ts index 338e8fd33..0c799287c 100644 --- a/packages/backend/src/server/api/endpoints/charts/users.ts +++ b/packages/backend/src/server/api/endpoints/charts/users.ts @@ -4,6 +4,7 @@ import define from '../../define.js'; export const meta = { tags: ['charts', 'users'], + requireCredentialPrivateMode: true, res: getJsonSchema(usersChart.schema), diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index b8614d6e1..a2cd24849 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -10,6 +10,7 @@ export const meta = { tags: ['account', 'notes', 'clips'], requireCredential: false, + requireCredentialPrivateMode: true, kind: 'read:account', diff --git a/packages/backend/src/server/api/endpoints/clips/show.ts b/packages/backend/src/server/api/endpoints/clips/show.ts index 88a5c2eeb..b996418ab 100644 --- a/packages/backend/src/server/api/endpoints/clips/show.ts +++ b/packages/backend/src/server/api/endpoints/clips/show.ts @@ -6,6 +6,7 @@ export const meta = { tags: ['clips', 'account'], requireCredential: false, + requireCredentialPrivateMode: true, kind: 'read:account', diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index ace3309ad..86410642f 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -6,6 +6,7 @@ export const meta = { tags: ['federation'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index 8bbb678ea..5ffc42d16 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -6,6 +6,7 @@ export const meta = { tags: ['federation'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index 2a517cf13..ab5adc1f8 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -6,6 +6,7 @@ export const meta = { tags: ['federation'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index 4768796a9..b99608d7f 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -6,6 +6,7 @@ export const meta = { tags: ['federation'], requireCredential: false, + requireCredentialPrivateMode: true, res: { oneOf: [{ diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 15a8dbb5f..97ecc9b53 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -6,6 +6,7 @@ export const meta = { tags: ['federation'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts index 835f34002..648af505a 100644 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts @@ -5,6 +5,7 @@ export const meta = { tags: ['gallery'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts index 552810e54..582459075 100644 --- a/packages/backend/src/server/api/endpoints/gallery/popular.ts +++ b/packages/backend/src/server/api/endpoints/gallery/popular.ts @@ -5,6 +5,7 @@ export const meta = { tags: ['gallery'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index 3a21afae1..7dec02765 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -4,6 +4,7 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { tags: ['gallery'], + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts index 5fa28b48b..c954e64de 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts @@ -6,6 +6,7 @@ export const meta = { tags: ['gallery'], requireCredential: false, + requireCredentialPrivateMode: true, errors: { noSuchPost: { diff --git a/packages/backend/src/server/api/endpoints/get-online-users-count.ts b/packages/backend/src/server/api/endpoints/get-online-users-count.ts index 56c550297..a8febe05b 100644 --- a/packages/backend/src/server/api/endpoints/get-online-users-count.ts +++ b/packages/backend/src/server/api/endpoints/get-online-users-count.ts @@ -7,6 +7,7 @@ export const meta = { tags: ['meta'], requireCredential: false, + requireCredentialPrivateMode: true, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/hashtags/list.ts b/packages/backend/src/server/api/endpoints/hashtags/list.ts index 4277410c8..02db6d518 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/list.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/list.ts @@ -5,6 +5,7 @@ export const meta = { tags: ['hashtags'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts index bc31b0419..e790cf1e3 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/search.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/search.ts @@ -5,6 +5,7 @@ export const meta = { tags: ['hashtags'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/hashtags/show.ts b/packages/backend/src/server/api/endpoints/hashtags/show.ts index ae3d1449f..72d368e35 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/show.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/show.ts @@ -7,6 +7,7 @@ export const meta = { tags: ['hashtags'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'object', diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index aee186401..ecdb48999 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -24,6 +24,7 @@ export const meta = { tags: ['hashtags'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index bc691b0c3..1ed8d3b94 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -4,6 +4,7 @@ import define from '../../define.js'; export const meta = { requireCredential: false, + requireCredentialPrivateMode: true, tags: ['hashtags', 'users'], diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 205d0916f..c73b8d739 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -277,7 +277,7 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +export default define(meta, paramDef, async (ps, me): Promise> => { const instance = await fetchMeta(true); const emojis = await Emojis.find({ @@ -294,7 +294,7 @@ export default define(meta, paramDef, async (ps, me) => { }, }); - return { + const response: Record = { maintainerName: instance.maintainerName, maintainerEmail: instance.maintainerEmail, @@ -305,6 +305,10 @@ export default define(meta, paramDef, async (ps, me) => { description: instance.description, langs: instance.langs, tosUrl: instance.ToSUrl, + + secureMode: instance.secureMode, + privateMode: instance.privateMode, + disableRegistration: instance.disableRegistration, disableLocalTimeline: instance.disableLocalTimeline, disableGlobalTimeline: instance.disableGlobalTimeline, @@ -322,7 +326,7 @@ export default define(meta, paramDef, async (ps, me) => { backgroundImageUrl: instance.backgroundImageUrl, logoImageUrl: instance.logoImageUrl, maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため - emojis: await Emojis.packMany(emojis), + emojis: instance.privateMode && !me ? [] : await Emojis.packMany(emojis), defaultLightTheme: instance.defaultLightTheme, defaultDarkTheme: instance.defaultDarkTheme, enableEmail: instance.enableEmail, @@ -335,16 +339,20 @@ export default define(meta, paramDef, async (ps, me) => { translatorAvailable: instance.deeplAuthKey != null, - pinnedPages: instance.pinnedPages, - pinnedClipId: instance.pinnedClipId, + pinnedPages: instance.privateMode && !me ? [] : instance.pinnedPages, + pinnedClipId: instance.privateMode && !me ? [] : instance.pinnedClipId, cacheRemoteFiles: instance.cacheRemoteFiles, requireSetup: (await Users.countBy({ host: IsNull(), })) === 0, + }; - proxyAccountName: instance.proxyAccountId ? (await Users.pack(instance.proxyAccountId).catch(() => null))?.username : null, - - features: { + if (ps.detail) { + if (!instance.privateMode || me) { + const proxyAccount = instance.proxyAccountId ? await Users.pack(instance.proxyAccountId).catch(() => null) : null; + response.proxyAccountName = proxyAccount ? proxyAccount.username : null; + } + response.features = { registration: !instance.disableRegistration, localTimeLine: !instance.disableLocalTimeline, globalTimeLine: !instance.disableGlobalTimeline, @@ -358,6 +366,8 @@ export default define(meta, paramDef, async (ps, me) => { discord: instance.enableDiscordIntegration, serviceWorker: instance.enableServiceWorker, miauth: true, - }, - }; + }; + } + + return response; }); diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 015b0338e..fc2bc3741 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -5,6 +5,7 @@ import { makePaginationQuery } from '../common/make-pagination-query.js'; export const meta = { tags: ['notes'], + requireCredentialPrivateMode: true, res: { type: 'array', optional: false, nullable: false, diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index afd7f41cf..03444daf6 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -9,6 +9,7 @@ export const meta = { tags: ['notes'], requireCredential: false, + requireCredentialPrivateMode: true, description: 'Get a list of children of a notes. Children includes replies as well as quote renotes that quote the respective post. A post will not be duplicated if it is a reply and a quote of a note in this thread. For depths larger than 1 the threading has to be computed by the client.', @@ -21,7 +22,7 @@ export const meta = { ref: 'Note', }, }, -} as const; +}; export const paramDef = { type: 'object', diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index 976c11260..514386d73 100644 --- a/packages/backend/src/server/api/endpoints/notes/clips.ts +++ b/packages/backend/src/server/api/endpoints/notes/clips.ts @@ -8,6 +8,7 @@ export const meta = { tags: ['clips', 'notes'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index 7ee052001..fa9b58848 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -8,6 +8,7 @@ export const meta = { tags: ['notes'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index dd9cc581a..0e4a454d7 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -7,6 +7,7 @@ export const meta = { tags: ['notes'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 3d32e7c7a..f8e71e84d 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -13,6 +13,7 @@ import { generateMutedRenotesQuery } from '../../common/generated-muted-renote-q export const meta = { tags: ['notes'], + requireCredentialPrivateMode: true, res: { type: 'array', optional: false, nullable: false, diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 6a65c028a..3ab1d1e5e 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -15,6 +15,7 @@ import { generateMutedRenotesQuery } from '../../common/generated-muted-renote-q export const meta = { tags: ['notes'], + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index d9388b47f..f1a203c36 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -9,6 +9,7 @@ export const meta = { tags: ['notes', 'reactions'], requireCredential: false, + requireCredentialPrivateMode: true, allowGet: true, cacheSec: 60, diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 1fa9c5230..2f662f355 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -11,6 +11,7 @@ export const meta = { tags: ['notes'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index ab0018f58..b05ef5914 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -9,6 +9,7 @@ export const meta = { tags: ['notes'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index ef9f6c39c..99670ae2f 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -10,6 +10,7 @@ import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { tags: ['notes', 'hashtags'], + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 1d393f796..4d9925d03 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -12,6 +12,7 @@ export const meta = { tags: ['notes'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index c9c148747..83a39a855 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -7,6 +7,7 @@ export const meta = { tags: ['notes'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'object', diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index a5eca5e99..ff5adf790 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -12,6 +12,7 @@ export const meta = { tags: ['notes'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'object', diff --git a/packages/backend/src/server/api/endpoints/pages/featured.ts b/packages/backend/src/server/api/endpoints/pages/featured.ts index 5a149a626..75580778b 100644 --- a/packages/backend/src/server/api/endpoints/pages/featured.ts +++ b/packages/backend/src/server/api/endpoints/pages/featured.ts @@ -5,6 +5,7 @@ export const meta = { tags: ['pages'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index 5d37e86b9..54ae43deb 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -8,6 +8,7 @@ export const meta = { tags: ['pages'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'object', diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index 41595b47d..d2ded60a1 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -9,6 +9,7 @@ export const meta = { tags: ['users'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index 99f3730e9..fdfbc8a6f 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -4,6 +4,7 @@ import define from '../define.js'; export const meta = { requireCredential: false, + requireCredentialPrivateMode: true, tags: ['meta'], } as const; diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 2d263cae8..6099d92a0 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -4,6 +4,7 @@ import define from '../define.js'; export const meta = { requireCredential: false, + requireCredentialPrivateMode: true, tags: ['meta'], diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index 3a8211374..d2f2ddcbf 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -7,6 +7,7 @@ export const meta = { tags: ['users'], requireCredential: false, + requireCredentialPrivateMode: true, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index 09fdf27c2..becfad52d 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -4,6 +4,7 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { tags: ['users', 'clips'], + requireCredentialPrivateMode: true, description: 'Show all clips this user owns.', diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 7f9f98076..4971d21b0 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -9,6 +9,7 @@ export const meta = { tags: ['users'], requireCredential: false, + requireCredentialPrivateMode: true, description: 'Show everyone that follows this user.', diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 0aaa810f7..043841aa4 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -9,6 +9,7 @@ export const meta = { tags: ['users'], requireCredential: false, + requireCredentialPrivateMode: true, description: 'Show everyone that this user is following.', diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts index 8184c4ce4..ec4b32f78 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -4,6 +4,7 @@ import { makePaginationQuery } from '../../../common/make-pagination-query.js'; export const meta = { tags: ['users', 'gallery'], + requireCredentialPrivateMode: true, description: 'Show all gallery posts by the given user.', diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index 56965d306..8cf3ea040 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -9,6 +9,7 @@ export const meta = { tags: ['users'], requireCredential: false, + requireCredentialPrivateMode: true, description: 'Get a list of other users that the specified user frequently replies to.', diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 9fa56fe83..1e205eec3 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -11,6 +11,7 @@ import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { tags: ['users', 'notes'], + requireCredentialPrivateMode: true, description: 'Show all notes that this user created.', res: { diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index b1d28af84..e1d876e6b 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -4,6 +4,7 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { tags: ['users', 'pages'], + requireCredentialPrivateMode: true, description: 'Show all pages this user created.', diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 4750dc4f9..144326958 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -8,6 +8,7 @@ export const meta = { tags: ['users', 'reactions'], requireCredential: false, + requireCredentialPrivateMode: true, description: 'Show all reactions this user made.', diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 5dd10bb1f..47c5f9c5b 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -7,6 +7,7 @@ export const meta = { tags: ['users'], requireCredential: false, + requireCredentialPrivateMode: true, description: 'Search for a user by username and/or host.', diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index ae7efac3a..5c499eeb4 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -7,6 +7,7 @@ export const meta = { tags: ['users'], requireCredential: false, + requireCredentialPrivateMode: true, description: 'Search for users.', diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 846d83b49..892e37bdf 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -10,6 +10,7 @@ export const meta = { tags: ['users'], requireCredential: false, + requireCredentialPrivateMode: true, description: 'Show the properties of a user.', diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts index 47f322ee9..a68b6ea40 100644 --- a/packages/backend/src/server/api/endpoints/users/stats.ts +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -7,6 +7,7 @@ export const meta = { tags: ['users'], requireCredential: false, + requireCredentialPrivateMode: true, description: 'Show statistics about a user.', -- 2.34.1 From 61b7c8ca538016524b16aaee578e4357073144ff Mon Sep 17 00:00:00 2001 From: nullobsi Date: Tue, 20 Jul 2021 13:08:21 -0700 Subject: [PATCH 4/5] Add secure mode settings to Security tab --- locales/en-US.yml | 7 ++++ locales/ja-JP.yml | 7 ++++ .../src/server/api/endpoints/admin/meta.ts | 19 +++++++++++ .../server/api/endpoints/admin/update-meta.ts | 17 ++++++++++ packages/client/src/pages/admin/security.vue | 32 +++++++++++++++++++ 5 files changed, 82 insertions(+) diff --git a/locales/en-US.yml b/locales/en-US.yml index 8fa7acf9a..7f1967cfa 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -829,6 +829,13 @@ middle: "Medium" low: "Low" emailNotConfiguredWarning: "Email address not set." ratio: "Ratio" +secureMode: "Secure Mode (Authorized Fetch)" +instanceSecurity: "Instance Security" +secureModeInfo: "Requests from other instances must be signed, otherwise notes won't be returned." +privateMode: "Private Mode" +privateModeInfo: "When enabled, only authorized instances may fetch notes. Hides all notes from public." +allowedInstances: "Allowed Instances" +allowedInstancesDescription: "Set the hosts of the instances you want to allow, separated by line. Valid in private mode only." previewNoteText: "Show preview" customCss: "Custom CSS" customCssWarn: "This setting should only be used if you know what it does. Entering\ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 6e821d051..d8a8c8195 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -771,6 +771,13 @@ middle: "中" low: "低" emailNotConfiguredWarning: "メールアドレスの設定がされていません。" ratio: "比率" +secureMode: "セキュアモード (Authorized Fetch)" +instanceSecurity: "インスタンスのセキュリティー" +secureModeInfo: "他のインスタンスからリクエストするときに、証明を付けなければ返送しません。" +privateMode: "非公開モード" +privateModeInfo: "有効にして、許可されているインスタンスのみがリクエストできます。すべてのノートが公開に非表示にします。" +allowedInstances: "許可されたインスタンス" +allowedInstancesDescription: "許可したいインスタンスのホストを改行で区切って設定します。非公開モードだけで有効です。" previewNoteText: "本文をプレビュー" customCss: "カスタムCSS" customCssWarn: "この設定は必ず知識のある方が行ってください。不適切な設定を行うとクライアントが正常に使用できなくなる恐れがあります。" diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 4e3f33cfc..9d257809d 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -153,6 +153,22 @@ export const meta = { optional: false, nullable: false, }, }, + allowedHosts: { + type: 'array', + optional: true, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, + privateMode: { + type: 'boolean', + optional: false, nullable: false, + }, + secureMode: { + type: 'boolean', + optional: false, nullable: false, + }, hcaptchaSecretKey: { type: 'string', optional: true, nullable: true, @@ -327,6 +343,9 @@ export default define(meta, paramDef, async (ps, me) => { pinnedUsers: instance.pinnedUsers, hiddenTags: instance.hiddenTags, blockedHosts: instance.blockedHosts, + allowedHosts: instance.allowedHosts, + privateMode: instance.privateMode, + secureMode: instance.secureMode, hcaptchaSecretKey: instance.hcaptchaSecretKey, recaptchaSecretKey: instance.recaptchaSecretKey, proxyAccountId: instance.proxyAccountId, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 93b261c18..2cfdb9e73 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -26,6 +26,11 @@ export const paramDef = { blockedHosts: { type: 'array', nullable: true, items: { type: 'string', } }, + allowedHosts: { type: 'array', nullable: true, items: { + type: 'string', + } }, + secureMode: { type: 'boolean', nullable: true }, + privateMode: { type: 'boolean', nullable: true }, themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' }, bannerUrl: { type: 'string', nullable: true }, iconUrl: { type: 'string', nullable: true }, @@ -131,6 +136,18 @@ export default define(meta, paramDef, async (ps, me) => { set.themeColor = ps.themeColor; } + if (Array.isArray(ps.allowedHosts)) { + set.allowedHosts = ps.allowedHosts.filter(Boolean); + } + + if (typeof ps.privateMode === 'boolean') { + set.privateMode = ps.privateMode; + } + + if (typeof ps.secureMode === 'boolean') { + set.secureMode = ps.secureMode; + } + if (ps.bannerUrl !== undefined) { set.bannerUrl = ps.bannerUrl; } diff --git a/packages/client/src/pages/admin/security.vue b/packages/client/src/pages/admin/security.vue index 314a16053..d9747850e 100644 --- a/packages/client/src/pages/admin/security.vue +++ b/packages/client/src/pages/admin/security.vue @@ -26,6 +26,26 @@ {{ i18n.ts.save }} + + + + +
+ + + + + + + + + + + + + {{ i18n.ts.save }} +
+
@@ -38,6 +58,7 @@ import FormFolder from '@/components/form/folder.vue'; import FormSuspense from '@/components/form/suspense.vue'; import FormInput from '@/components/form/input.vue'; import FormButton from '@/components/ui/button.vue'; +import FormTextarea from '@/components/form/textarea.vue'; import * as os from '@/os'; import { fetchInstance } from '@/instance'; import { i18n } from '@/i18n'; @@ -47,16 +68,27 @@ let summalyProxy: string = $ref(''); let enableHcaptcha: boolean = $ref(false); let enableRecaptcha: boolean = $ref(false); +let secureMode: boolean = $ref(false); +let privateMode: boolean = $ref(false); +let allowedHosts: string = $ref(''); + async function init(): Promise { const meta = await os.api('admin/meta'); summalyProxy = meta.summalyProxy; enableHcaptcha = meta.enableHcaptcha; enableRecaptcha = meta.enableRecaptcha; + + secureMode = meta.secureMode; + privateMode = meta.privateMode; + allowedHosts = meta.allowedHosts.join('\n'); } function save(): void { os.apiWithDialog('admin/update-meta', { summalyProxy, + secureMode, + privateMode, + allowedHosts: allowedHosts.split('\n'), }).then(() => { fetchInstance(); }); -- 2.34.1 From aa76c974f39e0e15f0e363817baa2a5593153f9b Mon Sep 17 00:00:00 2001 From: nullobsi Date: Wed, 25 Aug 2021 20:48:57 -0700 Subject: [PATCH 5/5] Skip rendering private data in privateMode Co-authored-by: Francis Dinh --- .../src/remote/activitypub/check-fetch.ts | 1 - packages/backend/src/server/activitypub.ts | 16 +++---- .../src/server/activitypub/featured.ts | 2 +- .../src/server/activitypub/followers.ts | 4 +- .../src/server/activitypub/following.ts | 4 +- .../backend/src/server/activitypub/outbox.ts | 8 ++-- packages/backend/src/server/web/index.ts | 44 +++++++++++++++++++ 7 files changed, 61 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/remote/activitypub/check-fetch.ts b/packages/backend/src/remote/activitypub/check-fetch.ts index 63103aa64..7dd9a51c0 100644 --- a/packages/backend/src/remote/activitypub/check-fetch.ts +++ b/packages/backend/src/remote/activitypub/check-fetch.ts @@ -7,7 +7,6 @@ import { toPuny } from '@/misc/convert-host.js'; import DbResolver from '@/remote/activitypub/db-resolver.js'; import { getApId } from '@/remote/activitypub/type.js'; - export default async function checkFetch(req: IncomingMessage): Promise { const meta = await fetchMeta(); if (meta.secureMode || meta.privateMode) { diff --git a/packages/backend/src/server/activitypub.ts b/packages/backend/src/server/activitypub.ts index dd66f29c4..c423b37f0 100644 --- a/packages/backend/src/server/activitypub.ts +++ b/packages/backend/src/server/activitypub.ts @@ -70,7 +70,7 @@ router.get('/notes/:note', async (ctx, next) => { if (!isActivityPubReq(ctx)) return await next(); const verify = await checkFetch(ctx.req); - if (verify != 200) { + if (verify !== 200) { ctx.status = verify; return; } @@ -87,7 +87,7 @@ router.get('/notes/:note', async (ctx, next) => { } // リモートだったらリダイレクト - if (note.userHost != null) { + if (note.userHost !== null) { if (note.uri == null || isSelfHost(note.userHost)) { ctx.status = 500; return; @@ -110,7 +110,7 @@ router.get('/notes/:note', async (ctx, next) => { // note activity router.get('/notes/:note/activity', async ctx => { const verify = await checkFetch(ctx.req); - if (verify != 200) { + if (verify !== 200) { ctx.status = verify; return; } @@ -160,7 +160,7 @@ router.get('/users/:user/publickey', async ctx => { } const verify = await checkFetch(ctx.req); - if (verify != 200) { + if (verify !== 200) { ctx.status = verify; return; } @@ -220,7 +220,7 @@ router.get('/users/:user', async (ctx, next) => { } const verify = await checkFetch(ctx.req); - if (verify != 200) { + if (verify !== 200) { ctx.status = verify; return; } @@ -246,7 +246,7 @@ router.get('/@:user', async (ctx, next) => { } const verify = await checkFetch(ctx.req); - if (verify != 200) { + if (verify !== 200) { ctx.status = verify; return; } @@ -291,7 +291,7 @@ router.get('/emojis/:emoji', async ctx => { // like router.get('/likes/:like', async ctx => { const verify = await checkFetch(ctx.req); - if (verify != 200) { + if (verify !== 200) { ctx.status = verify; return; } @@ -326,7 +326,7 @@ router.get('/likes/:like', async ctx => { // follow router.get('/follows/:follower/:followee', async ctx => { const verify = await checkFetch(ctx.req); - if (verify != 200) { + if (verify !== 200) { ctx.status = verify; return; } diff --git a/packages/backend/src/server/activitypub/featured.ts b/packages/backend/src/server/activitypub/featured.ts index 6d3680798..f3f9207b7 100644 --- a/packages/backend/src/server/activitypub/featured.ts +++ b/packages/backend/src/server/activitypub/featured.ts @@ -12,7 +12,7 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; export default async (ctx: Router.RouterContext) => { const verify = await checkFetch(ctx.req); - if (verify != 200) { + if (verify !== 200) { ctx.status = verify; return; } diff --git a/packages/backend/src/server/activitypub/followers.ts b/packages/backend/src/server/activitypub/followers.ts index 3c8ea9458..04a58dc57 100644 --- a/packages/backend/src/server/activitypub/followers.ts +++ b/packages/backend/src/server/activitypub/followers.ts @@ -14,7 +14,7 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; export default async (ctx: Router.RouterContext) => { const verify = await checkFetch(ctx.req); - if (verify != 200) { + if (verify !== 200) { ctx.status = verify; return; } @@ -22,7 +22,7 @@ export default async (ctx: Router.RouterContext) => { const userId = ctx.params.user; const cursor = ctx.request.query.cursor; - if (cursor != null && typeof cursor !== 'string') { + if (cursor !== null && typeof cursor !== 'string') { ctx.status = 400; return; } diff --git a/packages/backend/src/server/activitypub/following.ts b/packages/backend/src/server/activitypub/following.ts index 836cd4d26..2b6c64513 100644 --- a/packages/backend/src/server/activitypub/following.ts +++ b/packages/backend/src/server/activitypub/following.ts @@ -14,7 +14,7 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; export default async (ctx: Router.RouterContext) => { const verify = await checkFetch(ctx.req); - if (verify != 200) { + if (verify !== 200) { ctx.status = verify; return; } @@ -22,7 +22,7 @@ export default async (ctx: Router.RouterContext) => { const userId = ctx.params.user; const cursor = ctx.request.query.cursor; - if (cursor != null && typeof cursor !== 'string') { + if (cursor !== null && typeof cursor !== 'string') { ctx.status = 400; return; } diff --git a/packages/backend/src/server/activitypub/outbox.ts b/packages/backend/src/server/activitypub/outbox.ts index 5dfaff366..3c0379546 100644 --- a/packages/backend/src/server/activitypub/outbox.ts +++ b/packages/backend/src/server/activitypub/outbox.ts @@ -19,7 +19,7 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; export default async (ctx: Router.RouterContext) => { const verify = await checkFetch(ctx.req); - if (verify != 200) { + if (verify !== 200) { ctx.status = verify; return; } @@ -27,20 +27,20 @@ export default async (ctx: Router.RouterContext) => { const userId = ctx.params.user; const sinceId = ctx.request.query.since_id; - if (sinceId != null && typeof sinceId !== 'string') { + if (sinceId !== null && typeof sinceId !== 'string') { ctx.status = 400; return; } const untilId = ctx.request.query.until_id; - if (untilId != null && typeof untilId !== 'string') { + if (untilId !== null && typeof untilId !== 'string') { ctx.status = 400; return; } const page = ctx.request.query.page === 'true'; - if (countIf(x => x != null, [sinceId, untilId]) > 1) { + if (countIf(x => x !== null, [sinceId, untilId]) > 1) { ctx.status = 400; return; } diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index e97b14d8d..f23712f7b 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -24,6 +24,7 @@ import { getNoteSummary } from '@/misc/get-note-summary.js'; import { queues } from '@/queue/queues.js'; import { MINUTE, DAY } from '@/const.js'; import { genOpenapiSpec } from '../api/openapi/gen-spec.js'; +import meta from '../api/endpoints/meta.js'; import { urlPreviewHandler } from './url-preview.js'; import { manifestHandler } from './manifest.js'; import packFeed from './feed.js'; @@ -218,6 +219,10 @@ router.get('/api.json', async ctx => { }); const getFeed = async (acct: string) => { + const meta = await fetchMeta(); + if (meta.privateMode) { + return; + } const { username, host } = Acct.parse(acct); const user = await Users.findOneBy({ usernameLower: username.toLowerCase(), @@ -267,6 +272,12 @@ router.get('/@:user.json', async ctx => { //#region SSR (for crawlers) // User router.get(['/@:user', '/@:user/:sub'], async (ctx, next) => { + const meta = await fetchMeta(); + if (meta.privateMode) { + await next(); + return; + } + const { username, host } = Acct.parse(ctx.params.user); const user = await Users.findOneBy({ usernameLower: username.toLowerCase(), @@ -355,6 +366,12 @@ router.get('/notes/:note', async (ctx, next) => { // Page router.get('/@:user/pages/:page', async (ctx, next) => { + const meta = await fetchMeta(); + if (meta.privateMode) { + await next(); + return; + } + const { username, host } = Acct.parse(ctx.params.user); const user = await Users.findOneBy({ usernameLower: username.toLowerCase(), @@ -396,6 +413,12 @@ router.get('/@:user/pages/:page', async (ctx, next) => { // Clip // TODO: 非publicなclipのハンドリング router.get('/clips/:clip', async (ctx, next) => { + const meta = await fetchMeta(); + if (meta.privateMode) { + await next(); + return; + } + const clip = await Clips.findOneBy({ id: ctx.params.clip, }); @@ -409,6 +432,7 @@ router.get('/clips/:clip', async (ctx, next) => { profile, avatarUrl: await Users.getAvatarUrl(await Users.findOneByOrFail({ id: clip.userId })), instanceName: meta.name || 'FoundKey', + privateMode: meta.privateMode, icon: meta.iconUrl, themeColor: meta.themeColor, }); @@ -423,6 +447,12 @@ router.get('/clips/:clip', async (ctx, next) => { // Gallery post router.get('/gallery/:post', async (ctx, next) => { + const meta = await fetchMeta(); + if (meta.privateMode) { + await next(); + return; + } + const post = await GalleryPosts.findOneBy({ id: ctx.params.post }); if (post) { @@ -448,6 +478,12 @@ router.get('/gallery/:post', async (ctx, next) => { // Channel router.get('/channels/:channel', async (ctx, next) => { + const meta = await fetchMeta(); + if (meta.privateMode) { + await next(); + return; + } + const channel = await Channels.findOneBy({ id: ctx.params.channel, }); @@ -473,6 +509,10 @@ router.get('/channels/:channel', async (ctx, next) => { router.get('/_info_card_', async ctx => { const meta = await fetchMeta(true); + if (meta.privateMode) { + ctx.status = 403; + return; + } ctx.remove('X-Frame-Options'); @@ -513,6 +553,10 @@ router.get('/streaming', async ctx => { // Render base html for all requests router.get('(.*)', async ctx => { const meta = await fetchMeta(); + if (meta.privateMode) { + return; + } + await ctx.render('base', { img: meta.bannerUrl, title: meta.name || 'FoundKey', -- 2.34.1