diff --git a/crates/core/models/src/v0/admin.rs b/crates/core/models/src/v0/admin.rs index cf47e9e9..558c37a3 100644 --- a/crates/core/models/src/v0/admin.rs +++ b/crates/core/models/src/v0/admin.rs @@ -326,6 +326,7 @@ auto_derived! { DeleteAccount, DisableAccount, EmailChange, + EmailVerify, // Channels DeleteChannel, @@ -381,6 +382,7 @@ impl AdminAuditItemActions { AdminAuditItemActions::DeleteAccount => true, AdminAuditItemActions::DisableAccount => true, AdminAuditItemActions::EmailChange => true, + AdminAuditItemActions::EmailVerify => true, AdminAuditItemActions::DeleteChannel => true, AdminAuditItemActions::EditChannel => true, AdminAuditItemActions::WipeChannel => true, diff --git a/crates/delta/src/routes/admin/accounts/account_email_verify.rs b/crates/delta/src/routes/admin/accounts/account_email_verify.rs index e69de29b..62601486 100644 --- a/crates/delta/src/routes/admin/accounts/account_email_verify.rs +++ b/crates/delta/src/routes/admin/accounts/account_email_verify.rs @@ -0,0 +1,70 @@ +use crate::routes::admin::util::{ + create_audit_action, flatten_authorized_user, user_has_permission, +}; +use revolt_database::{util::reference::Reference, AdminAuthorization, Database, EmailVerification, MFATicket}; +use revolt_models::v0; +use revolt_result::{create_error, Result}; +use rocket::State; +use rocket_empty::EmptyResponse; +use revolt_database::util::email::normalise_email; + +/// Verify the email of an account. Requires ManageAccounts permissions +#[openapi(tag = "Admin")] +#[put("/accounts/email/?")] +pub async fn admin_account_email_verify( + db: &State, + auth: AdminAuthorization, + target: Reference<'_>, + case: Option<&str>, +) -> Result { + let user = flatten_authorized_user(&auth); + if !user_has_permission(user, v0::AdminUserPermissionFlags::ManageAccounts) { + return Err(create_error!(MissingPermission { + permission: "ManageAccounts".to_string() + })); + } + + let target = target.as_user(db).await?; + + if target.privileged { + return Err(create_error!(PrivilegedAccount)); + } + + let admin = db.admin_user_fetch(&target.id).await.ok(); + + if let Some(admin) = admin { + if user_has_permission(&admin, v0::AdminUserPermissionFlags::ManageAccounts) { + return Err(create_error!(PrivilegedAccount)); + } + } + + let mut account = db.fetch_account(&target.id).await?; + + // Update account email + if let EmailVerification::Moving { new_email, .. } = &account.verification { + account.email = new_email.clone(); + account.email_normalised = normalise_email(new_email.clone()); + } else { + let mut ticket = MFATicket::new(account.id.to_string(), false); + ticket.authorised = true; + ticket.save(db).await?; + }; + + // Mark as verified + account.verification = EmailVerification::Verified; + + // Save to database + account.save(db).await?; + + create_audit_action( + db, + &user.id, + v0::AdminAuditItemActions::DeleteAccount, + case, + Some(&target.id), + None, + ) + .await?; + + Ok(EmptyResponse) +} diff --git a/crates/delta/src/routes/admin/mod.rs b/crates/delta/src/routes/admin/mod.rs index 8db831bc..9ca4eca1 100644 --- a/crates/delta/src/routes/admin/mod.rs +++ b/crates/delta/src/routes/admin/mod.rs @@ -41,6 +41,7 @@ pub fn routes() -> (Vec, OpenApi) { accounts::account_delete::admin_account_delete, accounts::account_disable::admin_account_disable, accounts::account_email_change::admin_account_email_change, + accounts::account_email_verify::admin_account_email_verify, channels::actions::channel_delete::admin_delete_channel, channels::actions::channel_edit::admin_channel_edit, channels::actions::channel_wipe::admin_channel_wipe,