Skip to content

Commit

Permalink
Add admin.updateAccountEamil (bluesky-social#812)
Browse files Browse the repository at this point in the history
* -add admin capability to update account email

* pr feedback
  • Loading branch information
dholms authored Apr 13, 2023
1 parent 4aa9103 commit d8b50c7
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 0 deletions.
25 changes: 25 additions & 0 deletions lexicons/com/atproto/admin/updateAccountEmail.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"lexicon": 1,
"id": "com.atproto.admin.updateAccountEmail",
"defs": {
"main": {
"type": "procedure",
"description": "Administrative action to update an account's email",
"input": {
"encoding": "application/json",
"schema": {
"type": "object",
"required": ["account", "email"],
"properties": {
"account": {
"type": "string",
"format": "at-identifier",
"description": "The handle or DID of the repo."
},
"email": {"type": "string"}
}
}
}
}
}
}
13 changes: 13 additions & 0 deletions packages/api/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import * as ComAtprotoAdminResolveModerationReports from './types/com/atproto/ad
import * as ComAtprotoAdminReverseModerationAction from './types/com/atproto/admin/reverseModerationAction'
import * as ComAtprotoAdminSearchRepos from './types/com/atproto/admin/searchRepos'
import * as ComAtprotoAdminTakeModerationAction from './types/com/atproto/admin/takeModerationAction'
import * as ComAtprotoAdminUpdateAccountEmail from './types/com/atproto/admin/updateAccountEmail'
import * as ComAtprotoAdminUpdateAccountHandle from './types/com/atproto/admin/updateAccountHandle'
import * as ComAtprotoIdentityResolveHandle from './types/com/atproto/identity/resolveHandle'
import * as ComAtprotoIdentityUpdateHandle from './types/com/atproto/identity/updateHandle'
Expand Down Expand Up @@ -108,6 +109,7 @@ export * as ComAtprotoAdminResolveModerationReports from './types/com/atproto/ad
export * as ComAtprotoAdminReverseModerationAction from './types/com/atproto/admin/reverseModerationAction'
export * as ComAtprotoAdminSearchRepos from './types/com/atproto/admin/searchRepos'
export * as ComAtprotoAdminTakeModerationAction from './types/com/atproto/admin/takeModerationAction'
export * as ComAtprotoAdminUpdateAccountEmail from './types/com/atproto/admin/updateAccountEmail'
export * as ComAtprotoAdminUpdateAccountHandle from './types/com/atproto/admin/updateAccountHandle'
export * as ComAtprotoIdentityResolveHandle from './types/com/atproto/identity/resolveHandle'
export * as ComAtprotoIdentityUpdateHandle from './types/com/atproto/identity/updateHandle'
Expand Down Expand Up @@ -398,6 +400,17 @@ export class AdminNS {
})
}

updateAccountEmail(
data?: ComAtprotoAdminUpdateAccountEmail.InputSchema,
opts?: ComAtprotoAdminUpdateAccountEmail.CallOptions,
): Promise<ComAtprotoAdminUpdateAccountEmail.Response> {
return this._service.xrpc
.call('com.atproto.admin.updateAccountEmail', opts?.qp, data, opts)
.catch((e) => {
throw ComAtprotoAdminUpdateAccountEmail.toKnownErr(e)
})
}

updateAccountHandle(
data?: ComAtprotoAdminUpdateAccountHandle.InputSchema,
opts?: ComAtprotoAdminUpdateAccountHandle.CallOptions,
Expand Down
28 changes: 28 additions & 0 deletions packages/api/src/client/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,33 @@ export const schemaDict = {
},
},
},
ComAtprotoAdminUpdateAccountEmail: {
lexicon: 1,
id: 'com.atproto.admin.updateAccountEmail',
defs: {
main: {
type: 'procedure',
description: "Administrative action to update an account's email",
input: {
encoding: 'application/json',
schema: {
type: 'object',
required: ['account', 'email'],
properties: {
account: {
type: 'string',
format: 'at-identifier',
description: 'The handle or DID of the repo.',
},
email: {
type: 'string',
},
},
},
},
},
},
},
ComAtprotoAdminUpdateAccountHandle: {
lexicon: 1,
id: 'com.atproto.admin.updateAccountHandle',
Expand Down Expand Up @@ -4796,6 +4823,7 @@ export const ids = {
'com.atproto.admin.reverseModerationAction',
ComAtprotoAdminSearchRepos: 'com.atproto.admin.searchRepos',
ComAtprotoAdminTakeModerationAction: 'com.atproto.admin.takeModerationAction',
ComAtprotoAdminUpdateAccountEmail: 'com.atproto.admin.updateAccountEmail',
ComAtprotoAdminUpdateAccountHandle: 'com.atproto.admin.updateAccountHandle',
ComAtprotoIdentityResolveHandle: 'com.atproto.identity.resolveHandle',
ComAtprotoIdentityUpdateHandle: 'com.atproto.identity.updateHandle',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* GENERATED CODE - DO NOT MODIFY
*/
import { Headers, XRPCError } from '@atproto/xrpc'
import { ValidationResult, BlobRef } from '@atproto/lexicon'
import { isObj, hasProp } from '../../../../util'
import { lexicons } from '../../../../lexicons'
import { CID } from 'multiformats/cid'

export interface QueryParams {}

export interface InputSchema {
/** The handle or DID of the repo. */
account: string
email: string
[k: string]: unknown
}

export interface CallOptions {
headers?: Headers
qp?: QueryParams
encoding: 'application/json'
}

export interface Response {
success: boolean
headers: Headers
}

export function toKnownErr(e: any) {
if (e instanceof XRPCError) {
}
return e
}
2 changes: 2 additions & 0 deletions packages/pds/src/api/com/atproto/admin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import getModerationReports from './getModerationReports'
import disableInviteCodes from './disableInviteCodes'
import getInviteCodes from './getInviteCodes'
import updateAccountHandle from './updateAccountHandle'
import updateAccountEmail from './updateAccountEmail'

export default function (server: Server, ctx: AppContext) {
resolveModerationReports(server, ctx)
Expand All @@ -28,4 +29,5 @@ export default function (server: Server, ctx: AppContext) {
disableInviteCodes(server, ctx)
getInviteCodes(server, ctx)
updateAccountHandle(server, ctx)
updateAccountEmail(server, ctx)
}
21 changes: 21 additions & 0 deletions packages/pds/src/api/com/atproto/admin/updateAccountEmail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { InvalidRequestError } from '@atproto/xrpc-server'
import { Server } from '../../../../lexicon'
import AppContext from '../../../../context'

export default function (server: Server, ctx: AppContext) {
server.com.atproto.admin.updateAccountEmail({
auth: ctx.adminVerifier,
handler: async ({ input }) => {
await ctx.db.transaction(async (dbTxn) => {
const accntService = ctx.services.account(dbTxn)
const account = await accntService.getAccount(input.body.account)
if (!account) {
throw new InvalidRequestError(
`Account does not exist: ${input.body.account}`,
)
}
await accntService.updateEmail(account.did, input.body.email)
})
},
})
}
11 changes: 11 additions & 0 deletions packages/pds/src/lexicon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import * as ComAtprotoAdminResolveModerationReports from './types/com/atproto/ad
import * as ComAtprotoAdminReverseModerationAction from './types/com/atproto/admin/reverseModerationAction'
import * as ComAtprotoAdminSearchRepos from './types/com/atproto/admin/searchRepos'
import * as ComAtprotoAdminTakeModerationAction from './types/com/atproto/admin/takeModerationAction'
import * as ComAtprotoAdminUpdateAccountEmail from './types/com/atproto/admin/updateAccountEmail'
import * as ComAtprotoAdminUpdateAccountHandle from './types/com/atproto/admin/updateAccountHandle'
import * as ComAtprotoIdentityResolveHandle from './types/com/atproto/identity/resolveHandle'
import * as ComAtprotoIdentityUpdateHandle from './types/com/atproto/identity/updateHandle'
Expand Down Expand Up @@ -257,6 +258,16 @@ export class AdminNS {
return this._server.xrpc.method(nsid, cfg)
}

updateAccountEmail<AV extends AuthVerifier>(
cfg: ConfigOf<
AV,
ComAtprotoAdminUpdateAccountEmail.Handler<ExtractAuth<AV>>
>,
) {
const nsid = 'com.atproto.admin.updateAccountEmail' // @ts-ignore
return this._server.xrpc.method(nsid, cfg)
}

updateAccountHandle<AV extends AuthVerifier>(
cfg: ConfigOf<
AV,
Expand Down
28 changes: 28 additions & 0 deletions packages/pds/src/lexicon/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,33 @@ export const schemaDict = {
},
},
},
ComAtprotoAdminUpdateAccountEmail: {
lexicon: 1,
id: 'com.atproto.admin.updateAccountEmail',
defs: {
main: {
type: 'procedure',
description: "Administrative action to update an account's email",
input: {
encoding: 'application/json',
schema: {
type: 'object',
required: ['account', 'email'],
properties: {
account: {
type: 'string',
format: 'at-identifier',
description: 'The handle or DID of the repo.',
},
email: {
type: 'string',
},
},
},
},
},
},
},
ComAtprotoAdminUpdateAccountHandle: {
lexicon: 1,
id: 'com.atproto.admin.updateAccountHandle',
Expand Down Expand Up @@ -4796,6 +4823,7 @@ export const ids = {
'com.atproto.admin.reverseModerationAction',
ComAtprotoAdminSearchRepos: 'com.atproto.admin.searchRepos',
ComAtprotoAdminTakeModerationAction: 'com.atproto.admin.takeModerationAction',
ComAtprotoAdminUpdateAccountEmail: 'com.atproto.admin.updateAccountEmail',
ComAtprotoAdminUpdateAccountHandle: 'com.atproto.admin.updateAccountHandle',
ComAtprotoIdentityResolveHandle: 'com.atproto.identity.resolveHandle',
ComAtprotoIdentityUpdateHandle: 'com.atproto.identity.updateHandle',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* GENERATED CODE - DO NOT MODIFY
*/
import express from 'express'
import { ValidationResult, BlobRef } from '@atproto/lexicon'
import { lexicons } from '../../../../lexicons'
import { isObj, hasProp } from '../../../../util'
import { CID } from 'multiformats/cid'
import { HandlerAuth } from '@atproto/xrpc-server'

export interface QueryParams {}

export interface InputSchema {
/** The handle or DID of the repo. */
account: string
email: string
[k: string]: unknown
}

export interface HandlerInput {
encoding: 'application/json'
body: InputSchema
}

export interface HandlerError {
status: number
message?: string
}

export type HandlerOutput = HandlerError | void
export type Handler<HA extends HandlerAuth = never> = (ctx: {
auth: HA
params: QueryParams
input: HandlerInput
req: express.Request
res: express.Response
}) => Promise<HandlerOutput> | HandlerOutput
8 changes: 8 additions & 0 deletions packages/pds/src/services/account/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ export class AccountService {
await sequenceHandleUpdate(this.db, did, handle)
}

async updateEmail(did: string, email: string) {
await this.db.db
.updateTable('user_account')
.set({ email: email.toLowerCase() })
.where('did', '=', did)
.executeTakeFirst()
}

async updateUserPassword(did: string, password: string) {
const passwordScrypt = await scrypt.hash(password)
await this.db.db
Expand Down
30 changes: 30 additions & 0 deletions packages/pds/tests/account.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,36 @@ describe('account', () => {
])
})

it('allows administrative email updates', async () => {
await agent.api.com.atproto.admin.updateAccountEmail(
{
account: handle,
email: '[email protected]',
},
{
encoding: 'application/json',
headers: { authorization: util.adminAuth() },
},
)

const accnt = await ctx.services.account(ctx.db).getAccount(handle)
expect(accnt?.email).toBe('[email protected]')

await agent.api.com.atproto.admin.updateAccountEmail(
{
account: did,
email,
},
{
encoding: 'application/json',
headers: { authorization: util.adminAuth() },
},
)

const accnt2 = await ctx.services.account(ctx.db).getAccount(handle)
expect(accnt2?.email).toBe(email)
})

it('disallows duplicate email addresses and handles', async () => {
const inviteCode = await createInviteCode(agent, 2)
const email = '[email protected]'
Expand Down

0 comments on commit d8b50c7

Please sign in to comment.