feat(admin): add resend verification email op
This commit is contained in:
parent
2be274e762
commit
08eaad6f76
@ -106,6 +106,7 @@ import type {
|
||||
ListUserDmChannelsRequest,
|
||||
ListWebAuthnCredentialsRequest,
|
||||
LookupUserRequest,
|
||||
ResendVerificationEmailRequest,
|
||||
SendPasswordResetRequest,
|
||||
SetUserAclsRequest,
|
||||
SetUserBotStatusRequest,
|
||||
@ -378,6 +379,14 @@ export class AdminService {
|
||||
return this.userService.verifyUserEmail(data, adminUserId, auditLogReason);
|
||||
}
|
||||
|
||||
async resendVerificationEmail(
|
||||
data: ResendVerificationEmailRequest,
|
||||
adminUserId: UserID,
|
||||
auditLogReason: string | null,
|
||||
) {
|
||||
return this.userService.resendVerificationEmail(data, adminUserId, auditLogReason);
|
||||
}
|
||||
|
||||
async sendPasswordReset(data: SendPasswordResetRequest, adminUserId: UserID, auditLogReason: string | null) {
|
||||
return this.userService.sendPasswordReset(data, adminUserId, auditLogReason);
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@ import {
|
||||
ListWebAuthnCredentialsRequest,
|
||||
LookupUserRequest,
|
||||
LookupUserResponse,
|
||||
ResendVerificationEmailRequest,
|
||||
ScheduleAccountDeletionRequest,
|
||||
SendPasswordResetRequest,
|
||||
SetUserAclsRequest,
|
||||
@ -312,6 +313,30 @@ export function UserAdminController(app: HonoApp) {
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/users/resend-verification-email',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY),
|
||||
requireAdminACL(AdminACLs.USER_UPDATE_EMAIL),
|
||||
Validator('json', ResendVerificationEmailRequest),
|
||||
OpenAPI({
|
||||
operationId: 'admin_resend_verification_email',
|
||||
summary: 'Resend verification email',
|
||||
responseSchema: null,
|
||||
statusCode: 204,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
description:
|
||||
'Resend the account verification email for a user. Creates audit log entry and honours email verification resend limits. Requires USER_UPDATE_EMAIL permission.',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
await adminService.resendVerificationEmail(ctx.req.valid('json'), adminUserId, auditLogReason);
|
||||
return ctx.body(null, 204);
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/users/send-password-reset',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY),
|
||||
|
||||
@ -41,6 +41,7 @@ import type {
|
||||
DisableForSuspiciousActivityRequest,
|
||||
DisableMfaRequest,
|
||||
ListWebAuthnCredentialsRequest,
|
||||
ResendVerificationEmailRequest,
|
||||
SendPasswordResetRequest,
|
||||
SetUserAclsRequest,
|
||||
SetUserTraitsRequest,
|
||||
@ -186,6 +187,34 @@ export class AdminUserSecurityService {
|
||||
});
|
||||
}
|
||||
|
||||
async resendVerificationEmail(
|
||||
data: ResendVerificationEmailRequest,
|
||||
adminUserId: UserID,
|
||||
auditLogReason: string | null,
|
||||
) {
|
||||
const {userRepository, authService, auditService} = this.deps;
|
||||
const userId = createUserID(data.user_id);
|
||||
const user = await userRepository.findUnique(userId);
|
||||
if (!user) {
|
||||
throw new UnknownUserError();
|
||||
}
|
||||
|
||||
if (!user.email) {
|
||||
throw InputValidationError.fromCode('email', ValidationErrorCodes.USER_DOES_NOT_HAVE_AN_EMAIL_ADDRESS);
|
||||
}
|
||||
|
||||
await authService.resendVerificationEmail(user);
|
||||
|
||||
await auditService.createAuditLog({
|
||||
adminUserId,
|
||||
targetType: 'user',
|
||||
targetId: BigInt(userId),
|
||||
action: 'resend_verification_email',
|
||||
auditLogReason,
|
||||
metadata: new Map([['email', user.email]]),
|
||||
});
|
||||
}
|
||||
|
||||
async terminateSessions(data: TerminateSessionsRequest, adminUserId: UserID, auditLogReason: string | null) {
|
||||
const {userRepository, authService, auditService} = this.deps;
|
||||
const userId = createUserID(data.user_id);
|
||||
|
||||
@ -55,6 +55,7 @@ import type {
|
||||
ListUserDmChannelsRequest,
|
||||
ListWebAuthnCredentialsRequest,
|
||||
LookupUserRequest,
|
||||
ResendVerificationEmailRequest,
|
||||
ScheduleAccountDeletionRequest,
|
||||
SendPasswordResetRequest,
|
||||
SetUserAclsRequest,
|
||||
@ -221,6 +222,14 @@ export class AdminUserService {
|
||||
return this.securityService.sendPasswordReset(data, adminUserId, auditLogReason);
|
||||
}
|
||||
|
||||
async resendVerificationEmail(
|
||||
data: ResendVerificationEmailRequest,
|
||||
adminUserId: UserID,
|
||||
auditLogReason: string | null,
|
||||
) {
|
||||
return this.securityService.resendVerificationEmail(data, adminUserId, auditLogReason);
|
||||
}
|
||||
|
||||
async terminateSessions(data: TerminateSessionsRequest, adminUserId: UserID, auditLogReason: string | null) {
|
||||
return this.securityService.terminateSessions(data, adminUserId, auditLogReason);
|
||||
}
|
||||
|
||||
@ -272,6 +272,12 @@ export const VerifyUserEmailRequest = z.object({
|
||||
|
||||
export type VerifyUserEmailRequest = z.infer<typeof VerifyUserEmailRequest>;
|
||||
|
||||
export const ResendVerificationEmailRequest = z.object({
|
||||
user_id: SnowflakeType.describe('ID of the user to resend verification email to'),
|
||||
});
|
||||
|
||||
export type ResendVerificationEmailRequest = z.infer<typeof ResendVerificationEmailRequest>;
|
||||
|
||||
export const SendPasswordResetRequest = z.object({
|
||||
user_id: SnowflakeType.describe('ID of the user to send password reset to'),
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user