@atproto/pds 0.5.12 → 0.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/dist/api/app/bsky/notification/index.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/index.js +2 -0
- package/dist/api/app/bsky/notification/index.js.map +1 -1
- package/dist/api/app/bsky/notification/unregisterPush.d.ts +4 -0
- package/dist/api/app/bsky/notification/unregisterPush.d.ts.map +1 -0
- package/dist/api/app/bsky/notification/unregisterPush.js +48 -0
- package/dist/api/app/bsky/notification/unregisterPush.js.map +1 -0
- package/package.json +42 -38
- package/build.templates.cjs +0 -22
- package/example.env +0 -42
- package/jest.config.cjs +0 -27
- package/src/account-manager/account-manager.ts +0 -916
- package/src/account-manager/db/index.ts +0 -21
- package/src/account-manager/db/migrations/001-init.ts +0 -115
- package/src/account-manager/db/migrations/002-account-deactivation.ts +0 -17
- package/src/account-manager/db/migrations/003-privileged-app-passwords.ts +0 -12
- package/src/account-manager/db/migrations/004-oauth.ts +0 -122
- package/src/account-manager/db/migrations/005-oauth-account-management.ts +0 -136
- package/src/account-manager/db/migrations/006-oauth-permission-sets.ts +0 -20
- package/src/account-manager/db/migrations/007-lexicon-failures-index.ts +0 -14
- package/src/account-manager/db/migrations/index.ts +0 -17
- package/src/account-manager/db/schema/account-device.ts +0 -14
- package/src/account-manager/db/schema/account.ts +0 -16
- package/src/account-manager/db/schema/actor.ts +0 -17
- package/src/account-manager/db/schema/app-password.ts +0 -13
- package/src/account-manager/db/schema/authorization-request.ts +0 -30
- package/src/account-manager/db/schema/authorized-client.ts +0 -23
- package/src/account-manager/db/schema/device.ts +0 -18
- package/src/account-manager/db/schema/email-token.ts +0 -19
- package/src/account-manager/db/schema/index.ts +0 -43
- package/src/account-manager/db/schema/invite-code.ts +0 -24
- package/src/account-manager/db/schema/lexicon.ts +0 -15
- package/src/account-manager/db/schema/refresh-token.ts +0 -11
- package/src/account-manager/db/schema/repo-root.ts +0 -12
- package/src/account-manager/db/schema/token.ts +0 -38
- package/src/account-manager/db/schema/used-refresh-token.ts +0 -13
- package/src/account-manager/helpers/account-device.ts +0 -63
- package/src/account-manager/helpers/account.ts +0 -355
- package/src/account-manager/helpers/auth.ts +0 -222
- package/src/account-manager/helpers/authorization-request.ts +0 -90
- package/src/account-manager/helpers/authorized-client.ts +0 -75
- package/src/account-manager/helpers/device.ts +0 -47
- package/src/account-manager/helpers/email-token.ts +0 -87
- package/src/account-manager/helpers/invite.ts +0 -263
- package/src/account-manager/helpers/lexicon.ts +0 -67
- package/src/account-manager/helpers/password.ts +0 -136
- package/src/account-manager/helpers/repo.ts +0 -24
- package/src/account-manager/helpers/scrypt.ts +0 -53
- package/src/account-manager/helpers/token.ts +0 -158
- package/src/account-manager/helpers/used-refresh-token.ts +0 -30
- package/src/account-manager/oauth-store.ts +0 -880
- package/src/account-manager/scope-reference-getter.ts +0 -106
- package/src/actor-store/actor-store-reader.ts +0 -45
- package/src/actor-store/actor-store-resources.ts +0 -8
- package/src/actor-store/actor-store-transactor.ts +0 -31
- package/src/actor-store/actor-store-writer.ts +0 -17
- package/src/actor-store/actor-store.ts +0 -210
- package/src/actor-store/blob/reader.ts +0 -160
- package/src/actor-store/blob/transactor.ts +0 -383
- package/src/actor-store/db/index.ts +0 -20
- package/src/actor-store/db/migrations/001-init.ts +0 -105
- package/src/actor-store/db/migrations/index.ts +0 -5
- package/src/actor-store/db/schema/account-pref.ts +0 -11
- package/src/actor-store/db/schema/backlink.ts +0 -9
- package/src/actor-store/db/schema/blob.ts +0 -14
- package/src/actor-store/db/schema/index.ts +0 -23
- package/src/actor-store/db/schema/record-blob.ts +0 -8
- package/src/actor-store/db/schema/record.ts +0 -14
- package/src/actor-store/db/schema/repo-block.ts +0 -10
- package/src/actor-store/db/schema/repo-root.ts +0 -10
- package/src/actor-store/migrate.ts +0 -39
- package/src/actor-store/preference/reader.ts +0 -48
- package/src/actor-store/preference/transactor.ts +0 -55
- package/src/actor-store/preference/util.ts +0 -39
- package/src/actor-store/record/reader.ts +0 -374
- package/src/actor-store/record/transactor.ts +0 -111
- package/src/actor-store/repo/reader.ts +0 -31
- package/src/actor-store/repo/sql-repo-reader.ts +0 -150
- package/src/actor-store/repo/sql-repo-transactor.ts +0 -105
- package/src/actor-store/repo/transactor.ts +0 -227
- package/src/api/app/bsky/actor/getPreferences.ts +0 -57
- package/src/api/app/bsky/actor/getProfile.ts +0 -41
- package/src/api/app/bsky/actor/getProfiles.ts +0 -51
- package/src/api/app/bsky/actor/index.ts +0 -13
- package/src/api/app/bsky/actor/putPreferences.ts +0 -62
- package/src/api/app/bsky/feed/getActorLikes.ts +0 -63
- package/src/api/app/bsky/feed/getAuthorFeed.ts +0 -84
- package/src/api/app/bsky/feed/getFeed.ts +0 -47
- package/src/api/app/bsky/feed/getPostThread.ts +0 -251
- package/src/api/app/bsky/feed/getTimeline.ts +0 -50
- package/src/api/app/bsky/feed/index.ts +0 -15
- package/src/api/app/bsky/index.ts +0 -11
- package/src/api/app/bsky/notification/index.ts +0 -7
- package/src/api/app/bsky/notification/registerPush.ts +0 -71
- package/src/api/app/bsky/util/resolver.ts +0 -20
- package/src/api/com/atproto/admin/deleteAccount.ts +0 -22
- package/src/api/com/atproto/admin/disableAccountInvites.ts +0 -18
- package/src/api/com/atproto/admin/disableInviteCodes.ts +0 -21
- package/src/api/com/atproto/admin/enableAccountInvites.ts +0 -18
- package/src/api/com/atproto/admin/getAccountInfo.ts +0 -32
- package/src/api/com/atproto/admin/getAccountInfos.ts +0 -34
- package/src/api/com/atproto/admin/getInviteCodes.ts +0 -126
- package/src/api/com/atproto/admin/getSubjectStatus.ts +0 -76
- package/src/api/com/atproto/admin/index.ts +0 -31
- package/src/api/com/atproto/admin/sendEmail.ts +0 -47
- package/src/api/com/atproto/admin/updateAccountEmail.ts +0 -32
- package/src/api/com/atproto/admin/updateAccountHandle.ts +0 -47
- package/src/api/com/atproto/admin/updateAccountPassword.ts +0 -34
- package/src/api/com/atproto/admin/updateSubjectStatus.ts +0 -68
- package/src/api/com/atproto/admin/util.ts +0 -66
- package/src/api/com/atproto/identity/getRecommendedDidCredentials.ts +0 -50
- package/src/api/com/atproto/identity/index.ts +0 -17
- package/src/api/com/atproto/identity/requestPlcOperationSignature.ts +0 -59
- package/src/api/com/atproto/identity/resolveHandle.ts +0 -50
- package/src/api/com/atproto/identity/signPlcOperation.ts +0 -85
- package/src/api/com/atproto/identity/submitPlcOperation.ts +0 -65
- package/src/api/com/atproto/identity/updateHandle.ts +0 -66
- package/src/api/com/atproto/index.ts +0 -19
- package/src/api/com/atproto/moderation/createReport.ts +0 -41
- package/src/api/com/atproto/moderation/index.ts +0 -7
- package/src/api/com/atproto/repo/applyWrites.ts +0 -226
- package/src/api/com/atproto/repo/createRecord.ts +0 -143
- package/src/api/com/atproto/repo/deleteRecord.ts +0 -122
- package/src/api/com/atproto/repo/describeRepo.ts +0 -43
- package/src/api/com/atproto/repo/getRecord.ts +0 -40
- package/src/api/com/atproto/repo/importRepo.ts +0 -101
- package/src/api/com/atproto/repo/index.ts +0 -25
- package/src/api/com/atproto/repo/listMissingBlobs.ts +0 -29
- package/src/api/com/atproto/repo/listRecords.ts +0 -36
- package/src/api/com/atproto/repo/putRecord.ts +0 -211
- package/src/api/com/atproto/repo/uploadBlob.ts +0 -60
- package/src/api/com/atproto/server/activateAccount.ts +0 -37
- package/src/api/com/atproto/server/checkAccountStatus.ts +0 -51
- package/src/api/com/atproto/server/confirmEmail.ts +0 -39
- package/src/api/com/atproto/server/createAccount.ts +0 -327
- package/src/api/com/atproto/server/createAppPassword.ts +0 -53
- package/src/api/com/atproto/server/createInviteCode.ts +0 -38
- package/src/api/com/atproto/server/createInviteCodes.ts +0 -42
- package/src/api/com/atproto/server/createSession.ts +0 -97
- package/src/api/com/atproto/server/deactivateAccount.ts +0 -41
- package/src/api/com/atproto/server/deleteAccount.ts +0 -85
- package/src/api/com/atproto/server/deleteSession.ts +0 -23
- package/src/api/com/atproto/server/describeServer.ts +0 -29
- package/src/api/com/atproto/server/getAccountInviteCodes.ts +0 -142
- package/src/api/com/atproto/server/getServiceAuth.ts +0 -111
- package/src/api/com/atproto/server/getSession.ts +0 -80
- package/src/api/com/atproto/server/index.ts +0 -55
- package/src/api/com/atproto/server/listAppPasswords.ts +0 -45
- package/src/api/com/atproto/server/refreshSession.ts +0 -72
- package/src/api/com/atproto/server/requestAccountDelete.ts +0 -66
- package/src/api/com/atproto/server/requestEmailConfirmation.ts +0 -68
- package/src/api/com/atproto/server/requestEmailUpdate.ts +0 -86
- package/src/api/com/atproto/server/requestPasswordReset.ts +0 -52
- package/src/api/com/atproto/server/reserveSigningKey.ts +0 -15
- package/src/api/com/atproto/server/resetPassword.ts +0 -50
- package/src/api/com/atproto/server/revokeAppPassword.ts +0 -42
- package/src/api/com/atproto/server/updateEmail.ts +0 -52
- package/src/api/com/atproto/server/util.ts +0 -136
- package/src/api/com/atproto/sync/deprecated/getCheckout.ts +0 -27
- package/src/api/com/atproto/sync/deprecated/getHead.ts +0 -33
- package/src/api/com/atproto/sync/getBlob.ts +0 -61
- package/src/api/com/atproto/sync/getBlocks.ts +0 -37
- package/src/api/com/atproto/sync/getLatestCommit.ts +0 -33
- package/src/api/com/atproto/sync/getRecord.ts +0 -49
- package/src/api/com/atproto/sync/getRepo.ts +0 -75
- package/src/api/com/atproto/sync/getRepoStatus.ts +0 -34
- package/src/api/com/atproto/sync/index.ts +0 -27
- package/src/api/com/atproto/sync/listBlobs.ts +0 -33
- package/src/api/com/atproto/sync/listRepos.ts +0 -74
- package/src/api/com/atproto/sync/subscribeRepos.ts +0 -73
- package/src/api/com/atproto/sync/util.ts +0 -37
- package/src/api/com/atproto/temp/checkSignupQueue.ts +0 -34
- package/src/api/com/atproto/temp/index.ts +0 -7
- package/src/api/index.ts +0 -10
- package/src/api/proxy.ts +0 -44
- package/src/app-view.ts +0 -24
- package/src/auth-output.ts +0 -52
- package/src/auth-routes.ts +0 -64
- package/src/auth-scope.ts +0 -40
- package/src/auth-verifier.ts +0 -668
- package/src/background.ts +0 -65
- package/src/basic-routes.ts +0 -52
- package/src/bsky-app-view.ts +0 -33
- package/src/config/config.ts +0 -553
- package/src/config/env.ts +0 -167
- package/src/config/index.ts +0 -3
- package/src/config/secrets.ts +0 -54
- package/src/context.ts +0 -523
- package/src/crawlers.ts +0 -46
- package/src/db/cast.ts +0 -52
- package/src/db/db.ts +0 -140
- package/src/db/index.ts +0 -4
- package/src/db/migrator.ts +0 -40
- package/src/db/pagination.ts +0 -132
- package/src/db/tables/moderation.ts +0 -80
- package/src/db/util.ts +0 -108
- package/src/did-cache/db/index.ts +0 -21
- package/src/did-cache/db/migrations.ts +0 -17
- package/src/did-cache/db/schema.ts +0 -9
- package/src/did-cache/index.ts +0 -131
- package/src/disk-blobstore.ts +0 -172
- package/src/error.ts +0 -19
- package/src/handle/explicit-slurs.ts +0 -22
- package/src/handle/index.ts +0 -49
- package/src/handle/reserved.ts +0 -1059
- package/src/image/image-url-builder.ts +0 -16
- package/src/index.ts +0 -189
- package/src/lexicons.ts +0 -4
- package/src/logger.ts +0 -35
- package/src/mailer/index.ts +0 -111
- package/src/mailer/moderation.ts +0 -33
- package/src/mailer/templates/confirm-email.d.ts +0 -4
- package/src/mailer/templates/confirm-email.hbs +0 -158
- package/src/mailer/templates/delete-account.d.ts +0 -4
- package/src/mailer/templates/delete-account.hbs +0 -156
- package/src/mailer/templates/plc-operation.d.ts +0 -4
- package/src/mailer/templates/plc-operation.hbs +0 -153
- package/src/mailer/templates/reset-password.d.ts +0 -4
- package/src/mailer/templates/reset-password.hbs +0 -154
- package/src/mailer/templates/update-email.d.ts +0 -4
- package/src/mailer/templates/update-email.hbs +0 -153
- package/src/mailer/templates.ts +0 -17
- package/src/pipethrough.ts +0 -716
- package/src/rate-limits.ts +0 -59
- package/src/read-after-write/index.ts +0 -3
- package/src/read-after-write/types.ts +0 -35
- package/src/read-after-write/util.ts +0 -101
- package/src/read-after-write/viewer.ts +0 -290
- package/src/redis.ts +0 -25
- package/src/repo/index.ts +0 -2
- package/src/repo/prepare.ts +0 -266
- package/src/repo/types.ts +0 -65
- package/src/scripts/README.md +0 -40
- package/src/scripts/index.ts +0 -20
- package/src/scripts/publish-identity.ts +0 -60
- package/src/scripts/rebuild-repo.ts +0 -119
- package/src/scripts/rotate-keys.ts +0 -146
- package/src/scripts/sequencer-recovery/index.ts +0 -23
- package/src/scripts/sequencer-recovery/recoverer.ts +0 -297
- package/src/scripts/sequencer-recovery/recovery-db.ts +0 -65
- package/src/scripts/sequencer-recovery/repair-repos.ts +0 -49
- package/src/scripts/sequencer-recovery/user-queues.ts +0 -44
- package/src/scripts/util.ts +0 -7
- package/src/sequencer/db/index.ts +0 -21
- package/src/sequencer/db/migrations/001-init.ts +0 -35
- package/src/sequencer/db/migrations/index.ts +0 -5
- package/src/sequencer/db/schema.ts +0 -19
- package/src/sequencer/events.ts +0 -199
- package/src/sequencer/index.ts +0 -3
- package/src/sequencer/outbox.ts +0 -123
- package/src/sequencer/sequencer.ts +0 -290
- package/src/util/compression.ts +0 -16
- package/src/util/debug.ts +0 -10
- package/src/util/http.ts +0 -31
- package/src/util/types.ts +0 -7
- package/src/well-known.ts +0 -30
- package/test.env +0 -2
- package/tests/__snapshots__/takedown-appeal.test.ts.snap +0 -43
- package/tests/_oauth_client_assets_middleware.ts +0 -23
- package/tests/_puppeteer.ts +0 -147
- package/tests/_types.d.ts +0 -16
- package/tests/_util.ts +0 -191
- package/tests/account-deactivation.test.ts +0 -204
- package/tests/account-deletion.test.ts +0 -300
- package/tests/account-manager.test.ts +0 -462
- package/tests/account-migration.test.ts +0 -221
- package/tests/account-status.test.ts +0 -206
- package/tests/account.test.ts +0 -595
- package/tests/app-passwords.test.ts +0 -262
- package/tests/auth.test.ts +0 -405
- package/tests/blob-deletes.test.ts +0 -204
- package/tests/create-post.test.ts +0 -79
- package/tests/crud.test.ts +0 -1595
- package/tests/db.test.ts +0 -170
- package/tests/email-confirmation.test.ts +0 -227
- package/tests/entryway-mock.ts +0 -323
- package/tests/entryway.test.ts +0 -145
- package/tests/file-uploads.test.ts +0 -301
- package/tests/get-service-auth.test.ts +0 -81
- package/tests/handle-validation.test.ts +0 -56
- package/tests/handles.test.ts +0 -274
- package/tests/invite-codes.test.ts +0 -367
- package/tests/invites-admin.test.ts +0 -252
- package/tests/moderation.test.ts +0 -280
- package/tests/moderator-auth.test.ts +0 -177
- package/tests/oauth.test.ts +0 -334
- package/tests/plc-operations.test.ts +0 -239
- package/tests/preferences.test.ts +0 -352
- package/tests/proxied/__snapshots__/admin.test.ts.snap +0 -516
- package/tests/proxied/__snapshots__/feedgen.test.ts.snap +0 -215
- package/tests/proxied/__snapshots__/views.test.ts.snap +0 -4456
- package/tests/proxied/admin.test.ts +0 -340
- package/tests/proxied/feedgen.test.ts +0 -66
- package/tests/proxied/notif.test.ts +0 -85
- package/tests/proxied/procedures.test.ts +0 -175
- package/tests/proxied/proxy-catchall.test.ts +0 -256
- package/tests/proxied/proxy-header.test.ts +0 -239
- package/tests/proxied/proxy-oauth-aud.test.ts +0 -182
- package/tests/proxied/read-after-write.test.ts +0 -382
- package/tests/proxied/views.test.ts +0 -608
- package/tests/races.test.ts +0 -89
- package/tests/rate-limits.test.ts +0 -70
- package/tests/recovery.test.ts +0 -180
- package/tests/seeds/basic.ts +0 -165
- package/tests/seeds/follows.ts +0 -57
- package/tests/seeds/likes.ts +0 -14
- package/tests/seeds/reposts.ts +0 -18
- package/tests/seeds/thread.ts +0 -40
- package/tests/seeds/users-bulk.ts +0 -246
- package/tests/seeds/users.ts +0 -67
- package/tests/sequencer.test.ts +0 -251
- package/tests/server.test.ts +0 -151
- package/tests/sync/invertible-ops.test.ts +0 -99
- package/tests/sync/list.test.ts +0 -43
- package/tests/sync/subscribe-repos.test.ts +0 -672
- package/tests/sync/sync.test.ts +0 -316
- package/tests/takedown-appeal.test.ts +0 -166
- package/tsconfig.build.json +0 -9
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -8
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { DAY, HOUR } from '@atproto/common'
|
|
2
|
-
import {
|
|
3
|
-
MethodAuthVerifier,
|
|
4
|
-
MethodRateLimit,
|
|
5
|
-
Server,
|
|
6
|
-
} from '@atproto/xrpc-server'
|
|
7
|
-
import { AccessOutput, OAuthOutput } from '../../../../auth-output.js'
|
|
8
|
-
import { AppContext } from '../../../../context.js'
|
|
9
|
-
import { com } from '../../../../lexicons/index.js'
|
|
10
|
-
|
|
11
|
-
// Exposed as a utility to ensure auth in confirmEmail and requestEmailConfirmation
|
|
12
|
-
export function requestEmailConfirmationAuth(
|
|
13
|
-
ctx: AppContext,
|
|
14
|
-
): MethodAuthVerifier<AccessOutput | OAuthOutput> {
|
|
15
|
-
return ctx.authVerifier.authorization({
|
|
16
|
-
checkTakedown: true,
|
|
17
|
-
authorize: (permissions) => {
|
|
18
|
-
permissions.assertAccount({ attr: 'email', action: 'manage' })
|
|
19
|
-
},
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const rateLimit: MethodRateLimit<AccessOutput | OAuthOutput> = [
|
|
24
|
-
{
|
|
25
|
-
durationMs: DAY,
|
|
26
|
-
points: 15,
|
|
27
|
-
calcKey: ({ auth }) => auth.credentials.did,
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
durationMs: HOUR,
|
|
31
|
-
points: 5,
|
|
32
|
-
calcKey: ({ auth }) => auth.credentials.did,
|
|
33
|
-
},
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
export default function (server: Server, ctx: AppContext) {
|
|
37
|
-
const { entrywayClient } = ctx
|
|
38
|
-
|
|
39
|
-
const auth = requestEmailConfirmationAuth(ctx)
|
|
40
|
-
|
|
41
|
-
if (entrywayClient) {
|
|
42
|
-
server.add(com.atproto.server.requestEmailConfirmation, {
|
|
43
|
-
auth,
|
|
44
|
-
rateLimit,
|
|
45
|
-
handler: async ({ auth, req }) => {
|
|
46
|
-
const { headers } = await ctx.entrywayAuthHeaders(
|
|
47
|
-
req,
|
|
48
|
-
auth.credentials.did,
|
|
49
|
-
com.atproto.server.requestEmailConfirmation.$lxm,
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
await entrywayClient.xrpc(com.atproto.server.requestEmailConfirmation, {
|
|
53
|
-
headers,
|
|
54
|
-
})
|
|
55
|
-
},
|
|
56
|
-
})
|
|
57
|
-
} else {
|
|
58
|
-
server.add(com.atproto.server.requestEmailConfirmation, {
|
|
59
|
-
auth,
|
|
60
|
-
rateLimit,
|
|
61
|
-
handler: async ({ auth }) => {
|
|
62
|
-
const did = auth.credentials.did
|
|
63
|
-
const locale = undefined // @TODO get the locale somehow
|
|
64
|
-
await ctx.accountManager.requestEmailConfirmation(did, { locale })
|
|
65
|
-
},
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { DAY, HOUR } from '@atproto/common'
|
|
2
|
-
import {
|
|
3
|
-
ForbiddenError,
|
|
4
|
-
MethodAuthVerifier,
|
|
5
|
-
MethodRateLimit,
|
|
6
|
-
Server,
|
|
7
|
-
} from '@atproto/xrpc-server'
|
|
8
|
-
import { AccessOutput, OAuthOutput } from '../../../../auth-output.js'
|
|
9
|
-
import { ACCESS_FULL } from '../../../../auth-scope.js'
|
|
10
|
-
import { AppContext } from '../../../../context.js'
|
|
11
|
-
import { com } from '../../../../lexicons/index.js'
|
|
12
|
-
|
|
13
|
-
// Exposed as a utility to ensure auth in updateEmail and requestEmailUpdate
|
|
14
|
-
// stay consistent.
|
|
15
|
-
export function requestEmailUpdateAuth(
|
|
16
|
-
ctx: AppContext,
|
|
17
|
-
): MethodAuthVerifier<AccessOutput | OAuthOutput> {
|
|
18
|
-
return ctx.authVerifier.authorization({
|
|
19
|
-
checkTakedown: true,
|
|
20
|
-
scopes: ACCESS_FULL,
|
|
21
|
-
authorize: () => {
|
|
22
|
-
throw new ForbiddenError(
|
|
23
|
-
'Use the account manager interface to update email address associated with an account',
|
|
24
|
-
)
|
|
25
|
-
},
|
|
26
|
-
})
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const rateLimit: MethodRateLimit<AccessOutput | OAuthOutput> = [
|
|
30
|
-
{
|
|
31
|
-
durationMs: DAY,
|
|
32
|
-
points: 15,
|
|
33
|
-
calcKey: ({ auth }) => auth.credentials.did,
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
durationMs: HOUR,
|
|
37
|
-
points: 5,
|
|
38
|
-
calcKey: ({ auth }) => auth.credentials.did,
|
|
39
|
-
},
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
export default function (server: Server, ctx: AppContext) {
|
|
43
|
-
const { entrywayClient } = ctx
|
|
44
|
-
|
|
45
|
-
const auth = requestEmailUpdateAuth(ctx)
|
|
46
|
-
|
|
47
|
-
if (entrywayClient) {
|
|
48
|
-
server.add(com.atproto.server.requestEmailUpdate, {
|
|
49
|
-
rateLimit,
|
|
50
|
-
auth,
|
|
51
|
-
handler: async ({ auth, req }) => {
|
|
52
|
-
const { headers } = await ctx.entrywayAuthHeaders(
|
|
53
|
-
req,
|
|
54
|
-
auth.credentials.did,
|
|
55
|
-
com.atproto.server.requestEmailUpdate.$lxm,
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
return entrywayClient.xrpc(com.atproto.server.requestEmailUpdate, {
|
|
59
|
-
headers,
|
|
60
|
-
})
|
|
61
|
-
},
|
|
62
|
-
})
|
|
63
|
-
} else {
|
|
64
|
-
server.add(com.atproto.server.requestEmailUpdate, {
|
|
65
|
-
rateLimit,
|
|
66
|
-
auth,
|
|
67
|
-
handler: async ({ auth }) => {
|
|
68
|
-
const did = auth.credentials.did
|
|
69
|
-
|
|
70
|
-
// @TODO get the locale somehow (either by adding a field in the request
|
|
71
|
-
// body, or by using the `Accept-Language` header).
|
|
72
|
-
const locale = undefined
|
|
73
|
-
|
|
74
|
-
const { tokenRequired } = await ctx.accountManager.requestEmailUpdate(
|
|
75
|
-
did,
|
|
76
|
-
{ locale },
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
encoding: 'application/json' as const,
|
|
81
|
-
body: { tokenRequired },
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
})
|
|
85
|
-
}
|
|
86
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { DAY, HOUR } from '@atproto/common'
|
|
2
|
-
import { InvalidRequestError, Server } from '@atproto/xrpc-server'
|
|
3
|
-
import { AppContext } from '../../../../context.js'
|
|
4
|
-
import { com } from '../../../../lexicons/index.js'
|
|
5
|
-
|
|
6
|
-
export default function (server: Server, ctx: AppContext) {
|
|
7
|
-
const { entrywayClient } = ctx
|
|
8
|
-
|
|
9
|
-
server.add(com.atproto.server.requestPasswordReset, {
|
|
10
|
-
rateLimit: [
|
|
11
|
-
{
|
|
12
|
-
durationMs: DAY,
|
|
13
|
-
points: 50,
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
durationMs: HOUR,
|
|
17
|
-
points: 15,
|
|
18
|
-
},
|
|
19
|
-
],
|
|
20
|
-
handler: async ({ input: { body }, req }) => {
|
|
21
|
-
const email = body.email.toLowerCase()
|
|
22
|
-
|
|
23
|
-
const account = await ctx.accountManager.getAccountByEmail(email, {
|
|
24
|
-
includeDeactivated: true,
|
|
25
|
-
includeTakenDown: true,
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
if (account?.email) {
|
|
29
|
-
const token = await ctx.accountManager.createEmailToken(
|
|
30
|
-
account.did,
|
|
31
|
-
'reset_password',
|
|
32
|
-
)
|
|
33
|
-
await ctx.mailer.sendResetPassword(
|
|
34
|
-
{ handle: account.handle ?? account.email, token },
|
|
35
|
-
{ to: account.email },
|
|
36
|
-
)
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (entrywayClient) {
|
|
41
|
-
const { headers } = ctx.entrywayPassthruHeaders(req)
|
|
42
|
-
await entrywayClient.xrpc(com.atproto.server.requestPasswordReset, {
|
|
43
|
-
headers,
|
|
44
|
-
body,
|
|
45
|
-
})
|
|
46
|
-
return
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
throw new InvalidRequestError('account does not have an email address')
|
|
50
|
-
},
|
|
51
|
-
})
|
|
52
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Server } from '@atproto/xrpc-server'
|
|
2
|
-
import { AppContext } from '../../../../context.js'
|
|
3
|
-
import { com } from '../../../../lexicons/index.js'
|
|
4
|
-
|
|
5
|
-
export default function (server: Server, ctx: AppContext) {
|
|
6
|
-
server.add(com.atproto.server.reserveSigningKey, {
|
|
7
|
-
handler: async ({ input }) => {
|
|
8
|
-
const signingKey = await ctx.actorStore.reserveKeypair(input.body.did)
|
|
9
|
-
return {
|
|
10
|
-
encoding: 'application/json' as const,
|
|
11
|
-
body: { signingKey },
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
})
|
|
15
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { MINUTE } from '@atproto/common'
|
|
2
|
-
import {
|
|
3
|
-
InvalidRequestError,
|
|
4
|
-
MethodRateLimit,
|
|
5
|
-
Server,
|
|
6
|
-
} from '@atproto/xrpc-server'
|
|
7
|
-
import { NEW_PASSWORD_MAX_LENGTH } from '../../../../account-manager/helpers/scrypt.js'
|
|
8
|
-
import { AppContext } from '../../../../context.js'
|
|
9
|
-
import { com } from '../../../../lexicons/index.js'
|
|
10
|
-
|
|
11
|
-
export default function (server: Server, ctx: AppContext) {
|
|
12
|
-
const { entrywayClient } = ctx
|
|
13
|
-
|
|
14
|
-
const rateLimit: MethodRateLimit<
|
|
15
|
-
void,
|
|
16
|
-
com.atproto.server.resetPassword.$Params,
|
|
17
|
-
com.atproto.server.resetPassword.$Input
|
|
18
|
-
> = [
|
|
19
|
-
{
|
|
20
|
-
durationMs: 5 * MINUTE,
|
|
21
|
-
points: 50,
|
|
22
|
-
},
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
if (entrywayClient) {
|
|
26
|
-
server.add(com.atproto.server.resetPassword, {
|
|
27
|
-
rateLimit,
|
|
28
|
-
handler: async ({ input: { body }, req }) => {
|
|
29
|
-
const { headers } = ctx.entrywayPassthruHeaders(req)
|
|
30
|
-
await entrywayClient.xrpc(com.atproto.server.resetPassword, {
|
|
31
|
-
headers,
|
|
32
|
-
body,
|
|
33
|
-
})
|
|
34
|
-
},
|
|
35
|
-
})
|
|
36
|
-
} else {
|
|
37
|
-
server.add(com.atproto.server.resetPassword, {
|
|
38
|
-
rateLimit,
|
|
39
|
-
handler: async ({ input: { body } }) => {
|
|
40
|
-
const { token, password } = body
|
|
41
|
-
|
|
42
|
-
if (password.length > NEW_PASSWORD_MAX_LENGTH) {
|
|
43
|
-
throw new InvalidRequestError('Invalid password length.')
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
await ctx.accountManager.resetPassword({ token, password })
|
|
47
|
-
},
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { ForbiddenError, Server } from '@atproto/xrpc-server'
|
|
2
|
-
import { AppContext } from '../../../../context.js'
|
|
3
|
-
import { com } from '../../../../lexicons/index.js'
|
|
4
|
-
|
|
5
|
-
export default function (server: Server, ctx: AppContext) {
|
|
6
|
-
const { entrywayClient } = ctx
|
|
7
|
-
|
|
8
|
-
const auth = ctx.authVerifier.authorization({
|
|
9
|
-
authorize: () => {
|
|
10
|
-
throw new ForbiddenError(
|
|
11
|
-
'OAuth credentials are not supported for this endpoint',
|
|
12
|
-
)
|
|
13
|
-
},
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
if (entrywayClient) {
|
|
17
|
-
server.add(com.atproto.server.revokeAppPassword, {
|
|
18
|
-
auth,
|
|
19
|
-
handler: async ({ auth, input: { body }, req }) => {
|
|
20
|
-
const { headers } = await ctx.entrywayAuthHeaders(
|
|
21
|
-
req,
|
|
22
|
-
auth.credentials.did,
|
|
23
|
-
com.atproto.server.revokeAppPassword.$lxm,
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
await entrywayClient.xrpc(com.atproto.server.revokeAppPassword, {
|
|
27
|
-
headers,
|
|
28
|
-
body,
|
|
29
|
-
})
|
|
30
|
-
},
|
|
31
|
-
})
|
|
32
|
-
} else {
|
|
33
|
-
server.add(com.atproto.server.revokeAppPassword, {
|
|
34
|
-
auth,
|
|
35
|
-
handler: async ({ auth, input: { body } }) => {
|
|
36
|
-
const requester = auth.credentials.did
|
|
37
|
-
|
|
38
|
-
await ctx.accountManager.revokeAppPassword(requester, body.name)
|
|
39
|
-
},
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { InvalidRequestError, Server } from '@atproto/xrpc-server'
|
|
2
|
-
import { UserAlreadyExistsError } from '../../../../account-manager/helpers/account.js'
|
|
3
|
-
import { AppContext } from '../../../../context.js'
|
|
4
|
-
import { com } from '../../../../lexicons/index.js'
|
|
5
|
-
import { requestEmailUpdateAuth } from './requestEmailUpdate.js'
|
|
6
|
-
|
|
7
|
-
export default function (server: Server, ctx: AppContext) {
|
|
8
|
-
const { entrywayClient } = ctx
|
|
9
|
-
|
|
10
|
-
// @NOTE Ensure that both endpoints use the same authentication logic
|
|
11
|
-
const auth = requestEmailUpdateAuth(ctx)
|
|
12
|
-
|
|
13
|
-
if (entrywayClient) {
|
|
14
|
-
server.add(com.atproto.server.updateEmail, {
|
|
15
|
-
auth,
|
|
16
|
-
handler: async ({ auth, input: { body }, req }) => {
|
|
17
|
-
const { headers } = await ctx.entrywayAuthHeaders(
|
|
18
|
-
req,
|
|
19
|
-
auth.credentials.did,
|
|
20
|
-
com.atproto.server.updateEmail.$lxm,
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
await entrywayClient.xrpc(com.atproto.server.updateEmail, {
|
|
24
|
-
headers,
|
|
25
|
-
body,
|
|
26
|
-
})
|
|
27
|
-
},
|
|
28
|
-
})
|
|
29
|
-
} else {
|
|
30
|
-
server.add(com.atproto.server.updateEmail, {
|
|
31
|
-
auth,
|
|
32
|
-
handler: async ({ auth, input: { body } }) => {
|
|
33
|
-
const did = auth.credentials.did
|
|
34
|
-
const { token, email } = body
|
|
35
|
-
|
|
36
|
-
// @TODO get the locale somehow (either by adding a field in the request
|
|
37
|
-
// body, or by using the `Accept-Language` header).
|
|
38
|
-
const locale = undefined
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
await ctx.accountManager.updateEmail(did, email, token, { locale })
|
|
42
|
-
} catch (cause) {
|
|
43
|
-
if (cause instanceof UserAlreadyExistsError) {
|
|
44
|
-
throw new InvalidRequestError(cause.message, undefined, { cause })
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
throw cause
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
})
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import * as plc from '@did-plc/lib'
|
|
2
|
-
import { getPdsEndpoint, getSigningDidKey } from '@atproto/common'
|
|
3
|
-
import * as crypto from '@atproto/crypto'
|
|
4
|
-
import { DidDocument, IdResolver } from '@atproto/identity'
|
|
5
|
-
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
6
|
-
import { ActorStore } from '../../../../actor-store/actor-store.js'
|
|
7
|
-
import { ServerConfig } from '../../../../config/index.js'
|
|
8
|
-
import { httpLogger } from '../../../../logger.js'
|
|
9
|
-
|
|
10
|
-
// generate an invite code preceded by the hostname
|
|
11
|
-
// with '.'s replaced by '-'s so it is not mistakable for a link
|
|
12
|
-
// ex: bsky-app-abc23-567xy
|
|
13
|
-
// regex: bsky-app-[a-z2-7]{5}-[a-z2-7]{5}
|
|
14
|
-
export const genInvCode = (cfg: ServerConfig): string => {
|
|
15
|
-
return cfg.service.hostname.replaceAll('.', '-') + '-' + getRandomToken()
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const genInvCodes = (cfg: ServerConfig, count: number): string[] => {
|
|
19
|
-
const codes: string[] = []
|
|
20
|
-
for (let i = 0; i < count; i++) {
|
|
21
|
-
codes.push(genInvCode(cfg))
|
|
22
|
-
}
|
|
23
|
-
return codes
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Formatted xxxxx-xxxxx where digits are in base32
|
|
27
|
-
export const getRandomToken = () => {
|
|
28
|
-
const token = crypto.randomStr(8, 'base32').slice(0, 10)
|
|
29
|
-
return token.slice(0, 5) + '-' + token.slice(5, 10)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const safeResolveDidDoc = async (
|
|
33
|
-
ctx: { idResolver: IdResolver },
|
|
34
|
-
did: string,
|
|
35
|
-
forceRefresh?: boolean,
|
|
36
|
-
): Promise<DidDocument | undefined> => {
|
|
37
|
-
try {
|
|
38
|
-
const didDoc = await ctx.idResolver.did.resolve(did, forceRefresh)
|
|
39
|
-
return didDoc ?? undefined
|
|
40
|
-
} catch (err) {
|
|
41
|
-
httpLogger.warn({ err, did }, 'failed to resolve did doc')
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export const didDocForSession = async (
|
|
46
|
-
ctx: { idResolver: IdResolver; cfg: ServerConfig },
|
|
47
|
-
did: string,
|
|
48
|
-
forceRefresh?: boolean,
|
|
49
|
-
): Promise<DidDocument | undefined> => {
|
|
50
|
-
if (!ctx.cfg.identity.enableDidDocWithSession) return
|
|
51
|
-
return safeResolveDidDoc(ctx, did, forceRefresh)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export const isValidDidDocForService = async (
|
|
55
|
-
ctx: {
|
|
56
|
-
plcClient: plc.Client
|
|
57
|
-
idResolver: IdResolver
|
|
58
|
-
cfg: ServerConfig
|
|
59
|
-
plcRotationKey: crypto.Keypair
|
|
60
|
-
actorStore: ActorStore
|
|
61
|
-
},
|
|
62
|
-
did: string,
|
|
63
|
-
): Promise<boolean> => {
|
|
64
|
-
try {
|
|
65
|
-
await assertValidDidDocumentForService(ctx, did)
|
|
66
|
-
return true
|
|
67
|
-
} catch {
|
|
68
|
-
return false
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export const assertValidDidDocumentForService = async (
|
|
73
|
-
ctx: {
|
|
74
|
-
plcClient: plc.Client
|
|
75
|
-
idResolver: IdResolver
|
|
76
|
-
cfg: ServerConfig
|
|
77
|
-
plcRotationKey: crypto.Keypair
|
|
78
|
-
actorStore: ActorStore
|
|
79
|
-
},
|
|
80
|
-
did: string,
|
|
81
|
-
) => {
|
|
82
|
-
if (did.startsWith('did:plc')) {
|
|
83
|
-
const resolved = await ctx.plcClient.getDocumentData(did)
|
|
84
|
-
await assertValidDocContents(ctx, did, {
|
|
85
|
-
pdsEndpoint: resolved.services['atproto_pds']?.endpoint,
|
|
86
|
-
signingKey: resolved.verificationMethods['atproto'],
|
|
87
|
-
rotationKeys: resolved.rotationKeys,
|
|
88
|
-
})
|
|
89
|
-
} else {
|
|
90
|
-
const resolved = await ctx.idResolver.did.resolve(did, true)
|
|
91
|
-
if (!resolved) {
|
|
92
|
-
throw new InvalidRequestError('Could not resolve DID')
|
|
93
|
-
}
|
|
94
|
-
await assertValidDocContents(ctx, did, {
|
|
95
|
-
pdsEndpoint: getPdsEndpoint(resolved),
|
|
96
|
-
signingKey: getSigningDidKey(resolved),
|
|
97
|
-
})
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const assertValidDocContents = async (
|
|
102
|
-
ctx: {
|
|
103
|
-
cfg: ServerConfig
|
|
104
|
-
plcRotationKey: crypto.Keypair
|
|
105
|
-
actorStore: ActorStore
|
|
106
|
-
},
|
|
107
|
-
did: string,
|
|
108
|
-
contents: {
|
|
109
|
-
signingKey?: string
|
|
110
|
-
pdsEndpoint?: string
|
|
111
|
-
rotationKeys?: string[]
|
|
112
|
-
},
|
|
113
|
-
) => {
|
|
114
|
-
const { signingKey, pdsEndpoint, rotationKeys } = contents
|
|
115
|
-
|
|
116
|
-
const plcRotationKey =
|
|
117
|
-
ctx.cfg.entryway?.plcRotationKey ?? ctx.plcRotationKey.did()
|
|
118
|
-
if (rotationKeys !== undefined && !rotationKeys.includes(plcRotationKey)) {
|
|
119
|
-
throw new InvalidRequestError(
|
|
120
|
-
'Server rotation key not included in PLC DID data',
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (!pdsEndpoint || pdsEndpoint !== ctx.cfg.service.publicUrl) {
|
|
125
|
-
throw new InvalidRequestError(
|
|
126
|
-
'DID document atproto_pds service endpoint does not match PDS public url',
|
|
127
|
-
)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const keypair = await ctx.actorStore.keypair(did)
|
|
131
|
-
if (!signingKey || signingKey !== keypair.did()) {
|
|
132
|
-
throw new InvalidRequestError(
|
|
133
|
-
'DID document verification method does not match expected signing key',
|
|
134
|
-
)
|
|
135
|
-
}
|
|
136
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Server } from '@atproto/xrpc-server'
|
|
2
|
-
import { isUserOrAdmin } from '../../../../../auth-verifier.js'
|
|
3
|
-
import { AppContext } from '../../../../../context.js'
|
|
4
|
-
import { com } from '../../../../../lexicons/index.js'
|
|
5
|
-
import { getCarStream } from '../getRepo.js'
|
|
6
|
-
import { assertRepoAvailability } from '../util.js'
|
|
7
|
-
|
|
8
|
-
export default function (server: Server, ctx: AppContext) {
|
|
9
|
-
server.add(com.atproto.sync.getCheckout, {
|
|
10
|
-
auth: ctx.authVerifier.authorizationOrAdminTokenOptional({
|
|
11
|
-
authorize: () => {
|
|
12
|
-
// always allow
|
|
13
|
-
},
|
|
14
|
-
}),
|
|
15
|
-
handler: async ({ params, auth }) => {
|
|
16
|
-
const { did } = params
|
|
17
|
-
await assertRepoAvailability(ctx, did, isUserOrAdmin(auth, did))
|
|
18
|
-
|
|
19
|
-
const carStream = await getCarStream(ctx, did)
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
encoding: 'application/vnd.ipld.car' as const,
|
|
23
|
-
body: carStream,
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
})
|
|
27
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { InvalidRequestError, Server } from '@atproto/xrpc-server'
|
|
2
|
-
import { isUserOrAdmin } from '../../../../../auth-verifier.js'
|
|
3
|
-
import { AppContext } from '../../../../../context.js'
|
|
4
|
-
import { com } from '../../../../../lexicons/index.js'
|
|
5
|
-
import { assertRepoAvailability } from '../util.js'
|
|
6
|
-
|
|
7
|
-
export default function (server: Server, ctx: AppContext) {
|
|
8
|
-
server.add(com.atproto.sync.getHead, {
|
|
9
|
-
auth: ctx.authVerifier.authorizationOrAdminTokenOptional({
|
|
10
|
-
authorize: () => {
|
|
11
|
-
// always allow
|
|
12
|
-
},
|
|
13
|
-
}),
|
|
14
|
-
handler: async ({ params, auth }) => {
|
|
15
|
-
const { did } = params
|
|
16
|
-
await assertRepoAvailability(ctx, did, isUserOrAdmin(auth, did))
|
|
17
|
-
|
|
18
|
-
const root = await ctx.actorStore.read(did, (store) =>
|
|
19
|
-
store.repo.storage.getRoot(),
|
|
20
|
-
)
|
|
21
|
-
if (root === null) {
|
|
22
|
-
throw new InvalidRequestError(
|
|
23
|
-
`Could not find root for DID: ${did}`,
|
|
24
|
-
'HeadNotFound',
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
return {
|
|
28
|
-
encoding: 'application/json' as const,
|
|
29
|
-
body: { root: root.toString() },
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
})
|
|
33
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { parseCid } from '@atproto/lex-data'
|
|
2
|
-
import { BlobNotFoundError } from '@atproto/repo'
|
|
3
|
-
import { InvalidRequestError, Server } from '@atproto/xrpc-server'
|
|
4
|
-
import { AuthScope } from '../../../../auth-scope.js'
|
|
5
|
-
import { isUserOrAdmin } from '../../../../auth-verifier.js'
|
|
6
|
-
import { AppContext } from '../../../../context.js'
|
|
7
|
-
import { com } from '../../../../lexicons/index.js'
|
|
8
|
-
import { assertRepoAvailability } from './util.js'
|
|
9
|
-
|
|
10
|
-
export default function (server: Server, ctx: AppContext) {
|
|
11
|
-
server.add(com.atproto.sync.getBlob, {
|
|
12
|
-
auth: ctx.authVerifier.authorizationOrAdminTokenOptional({
|
|
13
|
-
additional: [AuthScope.Takendown],
|
|
14
|
-
authorize: () => {
|
|
15
|
-
// always allow
|
|
16
|
-
},
|
|
17
|
-
}),
|
|
18
|
-
handler: async ({ params, res, auth }) => {
|
|
19
|
-
const { did } = params
|
|
20
|
-
await assertRepoAvailability(ctx, did, isUserOrAdmin(auth, did))
|
|
21
|
-
|
|
22
|
-
const cid = parseCid(params.cid)
|
|
23
|
-
const found = await ctx.actorStore.read(params.did, async (store) => {
|
|
24
|
-
try {
|
|
25
|
-
return await store.repo.blob.getBlob(cid)
|
|
26
|
-
} catch (err) {
|
|
27
|
-
if (err instanceof BlobNotFoundError) {
|
|
28
|
-
throw new InvalidRequestError('Blob not found')
|
|
29
|
-
} else {
|
|
30
|
-
throw err
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
|
-
if (!found) {
|
|
35
|
-
throw new InvalidRequestError('Blob not found')
|
|
36
|
-
}
|
|
37
|
-
res.setHeader('content-length', found.size)
|
|
38
|
-
|
|
39
|
-
// Important Security headers
|
|
40
|
-
|
|
41
|
-
// This prevents the browser from trying to guess the content type
|
|
42
|
-
// and potentially loading the blob as executable code, or rendering it
|
|
43
|
-
// in some other unsafe way.
|
|
44
|
-
res.setHeader('x-content-type-options', 'nosniff')
|
|
45
|
-
|
|
46
|
-
// This forces the browser to download the blob instead of trying to
|
|
47
|
-
// render it when visiting the URL. This is important to prevent XSS
|
|
48
|
-
// attacks if the blob happens to be HTML. Even if JS is disabled via the
|
|
49
|
-
// CSP header below, a blob could still contain malicious HTML links.
|
|
50
|
-
res.setHeader('content-disposition', `attachment; filename="${cid}"`)
|
|
51
|
-
|
|
52
|
-
// This should prevent the browser from executing the blob in any way
|
|
53
|
-
res.setHeader('content-security-policy', `default-src 'none'; sandbox`)
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
encoding: found.mimeType || ('application/octet-stream' as const),
|
|
57
|
-
body: found.stream,
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
})
|
|
61
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { byteIterableToStream } from '@atproto/common'
|
|
2
|
-
import { parseCid } from '@atproto/lex-data'
|
|
3
|
-
import { blocksToCarStream } from '@atproto/repo'
|
|
4
|
-
import { InvalidRequestError, Server } from '@atproto/xrpc-server'
|
|
5
|
-
import { isUserOrAdmin } from '../../../../auth-verifier.js'
|
|
6
|
-
import { AppContext } from '../../../../context.js'
|
|
7
|
-
import { com } from '../../../../lexicons/index.js'
|
|
8
|
-
import { assertRepoAvailability } from './util.js'
|
|
9
|
-
|
|
10
|
-
export default function (server: Server, ctx: AppContext) {
|
|
11
|
-
server.add(com.atproto.sync.getBlocks, {
|
|
12
|
-
auth: ctx.authVerifier.authorizationOrAdminTokenOptional({
|
|
13
|
-
authorize: () => {
|
|
14
|
-
// always allow
|
|
15
|
-
},
|
|
16
|
-
}),
|
|
17
|
-
handler: async ({ params, auth }) => {
|
|
18
|
-
const { did } = params
|
|
19
|
-
await assertRepoAvailability(ctx, did, isUserOrAdmin(auth, did))
|
|
20
|
-
|
|
21
|
-
const cids = params.cids.map(parseCid)
|
|
22
|
-
const got = await ctx.actorStore.read(did, (store) =>
|
|
23
|
-
store.repo.storage.getBlocks(cids),
|
|
24
|
-
)
|
|
25
|
-
if (got.missing.length > 0) {
|
|
26
|
-
const missingStr = got.missing.map((c) => c.toString())
|
|
27
|
-
throw new InvalidRequestError(`Could not find cids: ${missingStr}`)
|
|
28
|
-
}
|
|
29
|
-
const car = blocksToCarStream(null, got.blocks)
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
encoding: 'application/vnd.ipld.car' as const,
|
|
33
|
-
body: byteIterableToStream(car),
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
})
|
|
37
|
-
}
|