feat(admin): add resend verification email op
This commit is contained in:
parent
2be274e762
commit
08eaad6f76
@ -106,6 +106,7 @@ import type {
|
|||||||
ListUserDmChannelsRequest,
|
ListUserDmChannelsRequest,
|
||||||
ListWebAuthnCredentialsRequest,
|
ListWebAuthnCredentialsRequest,
|
||||||
LookupUserRequest,
|
LookupUserRequest,
|
||||||
|
ResendVerificationEmailRequest,
|
||||||
SendPasswordResetRequest,
|
SendPasswordResetRequest,
|
||||||
SetUserAclsRequest,
|
SetUserAclsRequest,
|
||||||
SetUserBotStatusRequest,
|
SetUserBotStatusRequest,
|
||||||
@ -378,6 +379,14 @@ export class AdminService {
|
|||||||
return this.userService.verifyUserEmail(data, adminUserId, auditLogReason);
|
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) {
|
async sendPasswordReset(data: SendPasswordResetRequest, adminUserId: UserID, auditLogReason: string | null) {
|
||||||
return this.userService.sendPasswordReset(data, adminUserId, auditLogReason);
|
return this.userService.sendPasswordReset(data, adminUserId, auditLogReason);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,7 @@ import {
|
|||||||
ListWebAuthnCredentialsRequest,
|
ListWebAuthnCredentialsRequest,
|
||||||
LookupUserRequest,
|
LookupUserRequest,
|
||||||
LookupUserResponse,
|
LookupUserResponse,
|
||||||
|
ResendVerificationEmailRequest,
|
||||||
ScheduleAccountDeletionRequest,
|
ScheduleAccountDeletionRequest,
|
||||||
SendPasswordResetRequest,
|
SendPasswordResetRequest,
|
||||||
SetUserAclsRequest,
|
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(
|
app.post(
|
||||||
'/admin/users/send-password-reset',
|
'/admin/users/send-password-reset',
|
||||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY),
|
RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY),
|
||||||
|
|||||||
@ -41,6 +41,7 @@ import type {
|
|||||||
DisableForSuspiciousActivityRequest,
|
DisableForSuspiciousActivityRequest,
|
||||||
DisableMfaRequest,
|
DisableMfaRequest,
|
||||||
ListWebAuthnCredentialsRequest,
|
ListWebAuthnCredentialsRequest,
|
||||||
|
ResendVerificationEmailRequest,
|
||||||
SendPasswordResetRequest,
|
SendPasswordResetRequest,
|
||||||
SetUserAclsRequest,
|
SetUserAclsRequest,
|
||||||
SetUserTraitsRequest,
|
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) {
|
async terminateSessions(data: TerminateSessionsRequest, adminUserId: UserID, auditLogReason: string | null) {
|
||||||
const {userRepository, authService, auditService} = this.deps;
|
const {userRepository, authService, auditService} = this.deps;
|
||||||
const userId = createUserID(data.user_id);
|
const userId = createUserID(data.user_id);
|
||||||
|
|||||||
@ -55,6 +55,7 @@ import type {
|
|||||||
ListUserDmChannelsRequest,
|
ListUserDmChannelsRequest,
|
||||||
ListWebAuthnCredentialsRequest,
|
ListWebAuthnCredentialsRequest,
|
||||||
LookupUserRequest,
|
LookupUserRequest,
|
||||||
|
ResendVerificationEmailRequest,
|
||||||
ScheduleAccountDeletionRequest,
|
ScheduleAccountDeletionRequest,
|
||||||
SendPasswordResetRequest,
|
SendPasswordResetRequest,
|
||||||
SetUserAclsRequest,
|
SetUserAclsRequest,
|
||||||
@ -221,6 +222,14 @@ export class AdminUserService {
|
|||||||
return this.securityService.sendPasswordReset(data, adminUserId, auditLogReason);
|
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) {
|
async terminateSessions(data: TerminateSessionsRequest, adminUserId: UserID, auditLogReason: string | null) {
|
||||||
return this.securityService.terminateSessions(data, adminUserId, auditLogReason);
|
return this.securityService.terminateSessions(data, adminUserId, auditLogReason);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -272,6 +272,12 @@ export const VerifyUserEmailRequest = z.object({
|
|||||||
|
|
||||||
export type VerifyUserEmailRequest = z.infer<typeof VerifyUserEmailRequest>;
|
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({
|
export const SendPasswordResetRequest = z.object({
|
||||||
user_id: SnowflakeType.describe('ID of the user to send password reset to'),
|
user_id: SnowflakeType.describe('ID of the user to send password reset to'),
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user