From 067eb645bafb87222630db02e9d59107a7c7ced6 Mon Sep 17 00:00:00 2001 From: devin ivy Date: Sat, 4 Nov 2023 14:42:06 -0400 Subject: [PATCH] Entryway proxying for admin subject status endpoints (#1805) handle entryway proxying for admin subject status endpoints --- .../api/com/atproto/admin/getSubjectStatus.ts | 57 +++++++++++----- .../com/atproto/admin/updateSubjectStatus.ts | 68 +++++++++++++------ 2 files changed, 88 insertions(+), 37 deletions(-) diff --git a/packages/pds/src/api/com/atproto/admin/getSubjectStatus.ts b/packages/pds/src/api/com/atproto/admin/getSubjectStatus.ts index 20ded7bc747..468c5d707f5 100644 --- a/packages/pds/src/api/com/atproto/admin/getSubjectStatus.ts +++ b/packages/pds/src/api/com/atproto/admin/getSubjectStatus.ts @@ -5,35 +5,42 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' import { OutputSchema } from '../../../../lexicon/types/com/atproto/admin/getSubjectStatus' import { ensureValidAdminAud } from '../../../../auth-verifier' +import { authPassthru, proxy, resultPassthru } from '../../../proxy' export default function (server: Server, ctx: AppContext) { server.com.atproto.admin.getSubjectStatus({ auth: ctx.authVerifier.roleOrAdminService, - handler: async ({ params, auth }) => { - const { did, uri, blob } = params + handler: async ({ params, auth, req }) => { const modSrvc = ctx.services.moderation(ctx.db) + const accSrvc = ctx.services.account(ctx.db) + const { did, uri, blob } = parseSubject(params) + ensureValidAdminAud(auth, did) + + const account = await accSrvc.getAccount(did, true) + const proxied = await proxy(ctx, account?.pdsDid, async (agent) => { + const result = await agent.api.com.atproto.admin.getSubjectStatus( + params, + authPassthru(req), + ) + return resultPassthru(result) + }) + if (proxied !== null) { + return proxied + } + let body: OutputSchema | null if (blob) { - if (!did) { - throw new InvalidRequestError( - 'Must provide a did to request blob state', - ) - } - ensureValidAdminAud(auth, did) - body = await modSrvc.getBlobTakedownState(did, CID.parse(blob)) + body = await modSrvc.getBlobTakedownState(did, blob) } else if (uri) { - const parsedUri = new AtUri(uri) - ensureValidAdminAud(auth, parsedUri.hostname) - body = await modSrvc.getRecordTakedownState(parsedUri) - } else if (did) { - ensureValidAdminAud(auth, did) - body = await modSrvc.getRepoTakedownState(did) + body = await modSrvc.getRecordTakedownState(uri) } else { - throw new InvalidRequestError('No provided subject') + body = await modSrvc.getRepoTakedownState(did) } + if (body === null) { throw new InvalidRequestError('Subject not found', 'NotFound') } + return { encoding: 'application/json', body, @@ -41,3 +48,21 @@ export default function (server: Server, ctx: AppContext) { }, }) } + +const parseSubject = (opts: { did?: string; uri?: string; blob?: string }) => { + const { did, uri, blob } = opts + if (blob) { + if (!did) { + throw new InvalidRequestError('Must provide a did to request blob state') + } + const blobCid = CID.parse(blob) + return { did, blob: blobCid } + } else if (uri) { + const parsedUri = new AtUri(uri) + return { did: parsedUri.hostname, uri: parsedUri } + } else if (did) { + return { did } + } else { + throw new InvalidRequestError('No provided subject') + } +} diff --git a/packages/pds/src/api/com/atproto/admin/updateSubjectStatus.ts b/packages/pds/src/api/com/atproto/admin/updateSubjectStatus.ts index 920debba986..93774732db6 100644 --- a/packages/pds/src/api/com/atproto/admin/updateSubjectStatus.ts +++ b/packages/pds/src/api/com/atproto/admin/updateSubjectStatus.ts @@ -1,19 +1,21 @@ import { CID } from 'multiformats/cid' import { AtUri } from '@atproto/syntax' +import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../lexicon' import AppContext from '../../../../context' import { isRepoRef, isRepoBlobRef, } from '../../../../lexicon/types/com/atproto/admin/defs' +import { InputSchema } from '../../../../lexicon/types/com/atproto/admin/updateSubjectStatus' import { isMain as isStrongRef } from '../../../../lexicon/types/com/atproto/repo/strongRef' -import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' import { ensureValidAdminAud } from '../../../../auth-verifier' +import { authPassthru, proxy, resultPassthru } from '../../../proxy' export default function (server: Server, ctx: AppContext) { server.com.atproto.admin.updateSubjectStatus({ auth: ctx.authVerifier.roleOrAdminService, - handler: async ({ input, auth }) => { + handler: async ({ input, auth, req }) => { // if less than moderator access then cannot perform a takedown if (auth.credentials.type === 'role' && !auth.credentials.moderator) { throw new AuthRequiredError( @@ -22,28 +24,38 @@ export default function (server: Server, ctx: AppContext) { } const { subject, takedown } = input.body + const { did, uri, blob } = parseSubject(subject) + ensureValidAdminAud(auth, did) + + const modSrvc = ctx.services.moderation(ctx.db) + const authSrvc = ctx.services.auth(ctx.db) + const accSrvc = ctx.services.account(ctx.db) + + // no need to check if proxying actually occurred or use its result + const account = await accSrvc.getAccount(did, true) + const proxied = await proxy(ctx, account?.pdsDid, async (agent) => { + const result = await agent.api.com.atproto.admin.updateSubjectStatus( + input.body, + authPassthru(req, true), + ) + return resultPassthru(result) + }) + if (takedown) { - const modSrvc = ctx.services.moderation(ctx.db) - const authSrvc = ctx.services.auth(ctx.db) - if (isRepoRef(subject)) { - ensureValidAdminAud(auth, subject.did) + if (blob) { + if (!proxied) { + await modSrvc.updateBlobTakedownState(did, blob, takedown) + } + } else if (uri) { + if (!proxied) { + await modSrvc.updateRecordTakedownState(uri, takedown) + } + } else { + // apply account takedown on entryway in addition to proxied pds await Promise.all([ - modSrvc.updateRepoTakedownState(subject.did, takedown), - authSrvc.revokeRefreshTokensByDid(subject.did), + modSrvc.updateRepoTakedownState(did, takedown), + authSrvc.revokeRefreshTokensByDid(did), ]) - } else if (isStrongRef(subject)) { - const uri = new AtUri(subject.uri) - ensureValidAdminAud(auth, uri.hostname) - await modSrvc.updateRecordTakedownState(uri, takedown) - } else if (isRepoBlobRef(subject)) { - ensureValidAdminAud(auth, subject.did) - await modSrvc.updateBlobTakedownState( - subject.did, - CID.parse(subject.cid), - takedown, - ) - } else { - throw new InvalidRequestError('Invalid subject') } } @@ -57,3 +69,17 @@ export default function (server: Server, ctx: AppContext) { }, }) } + +const parseSubject = (subject: InputSchema['subject']) => { + if (isRepoRef(subject)) { + return { did: subject.did } + } else if (isStrongRef(subject)) { + const uri = new AtUri(subject.uri) + return { did: uri.hostname, uri } + } else if (isRepoBlobRef(subject)) { + const blobCid = CID.parse(subject.cid) + return { did: subject.did, blob: blobCid } + } else { + throw new InvalidRequestError('Invalid subject') + } +}