Skip to content

Commit

Permalink
Entryway proxying for admin subject status endpoints (#1805)
Browse files Browse the repository at this point in the history
handle entryway proxying for admin subject status endpoints
  • Loading branch information
devinivy authored Nov 4, 2023
1 parent 0fe35b6 commit 067eb64
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 37 deletions.
57 changes: 41 additions & 16 deletions packages/pds/src/api/com/atproto/admin/getSubjectStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,64 @@ 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,
}
},
})
}

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')
}
}
68 changes: 47 additions & 21 deletions packages/pds/src/api/com/atproto/admin/updateSubjectStatus.ts
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -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')
}
}

Expand All @@ -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')
}
}

0 comments on commit 067eb64

Please sign in to comment.