Add admin.updateAccountEamil (#812)
* -add admin capability to update account email * pr feedback
This commit is contained in:
parent
4aa9103fb5
commit
d8b50c73e4
lexicons/com/atproto/admin
packages
api/src/client
pds
src
api/com/atproto/admin
lexicon
services/account
tests
25
lexicons/com/atproto/admin/updateAccountEmail.json
Normal file
25
lexicons/com/atproto/admin/updateAccountEmail.json
Normal file
@ -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"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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'
|
||||
@ -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'
|
||||
@ -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,
|
||||
|
@ -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',
|
||||
@ -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',
|
||||
|
@ -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
|
||||
}
|
@ -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)
|
||||
@ -28,4 +29,5 @@ export default function (server: Server, ctx: AppContext) {
|
||||
disableInviteCodes(server, ctx)
|
||||
getInviteCodes(server, ctx)
|
||||
updateAccountHandle(server, ctx)
|
||||
updateAccountEmail(server, ctx)
|
||||
}
|
||||
|
21
packages/pds/src/api/com/atproto/admin/updateAccountEmail.ts
Normal file
21
packages/pds/src/api/com/atproto/admin/updateAccountEmail.ts
Normal file
@ -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)
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
@ -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'
|
||||
@ -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,
|
||||
|
@ -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',
|
||||
@ -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',
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -168,6 +168,36 @@ describe('account', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('allows administrative email updates', async () => {
|
||||
await agent.api.com.atproto.admin.updateAccountEmail(
|
||||
{
|
||||
account: handle,
|
||||
email: 'alIce-NEw@teST.com',
|
||||
},
|
||||
{
|
||||
encoding: 'application/json',
|
||||
headers: { authorization: util.adminAuth() },
|
||||
},
|
||||
)
|
||||
|
||||
const accnt = await ctx.services.account(ctx.db).getAccount(handle)
|
||||
expect(accnt?.email).toBe('alice-new@test.com')
|
||||
|
||||
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 = 'bob@test.com'
|
||||
|
Loading…
x
Reference in New Issue
Block a user