From 0e5ca572d0a1963a3b3513d18007dfe3e46450ea Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 8 Nov 2024 13:58:57 +0100 Subject: [PATCH 1/2] models/user: Add `async_email()` fn This is a clone of `email()`, but using `diesel_async` --- src/models/user.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/models/user.rs b/src/models/user.rs index 88a7307deb..05cd1dc29e 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -113,6 +113,17 @@ impl User { .first(conn) .optional() } + + /// Queries for the email belonging to a particular user + pub async fn async_email(&self, conn: &mut AsyncPgConnection) -> QueryResult> { + use diesel_async::RunQueryDsl; + + Email::belonging_to(self) + .select(emails::email) + .first(conn) + .await + .optional() + } } /// Represents a new user record insertable to the `users` table From 45e5225ed70fee718101531223b1e5c924382612 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Sat, 16 Nov 2024 18:30:08 +0100 Subject: [PATCH 2/2] controllers/github/secret_scanning: Migrate to `diesel-async` queries --- src/controllers/github/secret_scanning.rs | 61 +++++++++++------------ 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/controllers/github/secret_scanning.rs b/src/controllers/github/secret_scanning.rs index f02217bdf9..b0d8ff40d4 100644 --- a/src/controllers/github/secret_scanning.rs +++ b/src/controllers/github/secret_scanning.rs @@ -2,9 +2,7 @@ use crate::app::AppState; use crate::email::Email; use crate::models::{ApiToken, User}; use crate::schema::api_tokens; -use crate::tasks::spawn_blocking; use crate::util::diesel::prelude::*; -use crate::util::diesel::Conn; use crate::util::errors::{bad_request, AppResult, BoxedAppError}; use crate::util::token::HashedToken; use anyhow::{anyhow, Context}; @@ -12,7 +10,7 @@ use axum::body::Bytes; use axum::Json; use base64::{engine::general_purpose, Engine}; use crates_io_github::GitHubPublicKey; -use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; +use diesel_async::AsyncPgConnection; use http::HeaderMap; use p256::ecdsa::signature::Verifier; use p256::ecdsa::VerifyingKey; @@ -129,12 +127,12 @@ struct GitHubSecretAlert { } /// Revokes an API token and notifies the token owner -fn alert_revoke_token( +async fn alert_revoke_token( state: &AppState, alert: &GitHubSecretAlert, - conn: &mut impl Conn, + conn: &mut AsyncPgConnection, ) -> QueryResult { - use diesel::RunQueryDsl; + use diesel_async::RunQueryDsl; let hashed_token = HashedToken::hash(&alert.token); @@ -143,6 +141,7 @@ fn alert_revoke_token( .select(ApiToken::as_select()) .filter(api_tokens::token.eq(hashed_token)) .get_result::(conn) + .await .optional()?; let Some(token) = token else { @@ -160,14 +159,15 @@ fn alert_revoke_token( diesel::update(&token) .set(api_tokens::revoked.eq(true)) - .execute(conn)?; + .execute(conn) + .await?; warn!( token_id = %token.id, user_id = %token.user_id, "Active API token received and revoked (true positive)", ); - if let Err(error) = send_notification_email(&token, alert, state, conn) { + if let Err(error) = send_notification_email(&token, alert, state, conn).await { warn!( token_id = %token.id, user_id = %token.user_id, ?error, "Failed to send email notification", @@ -177,14 +177,17 @@ fn alert_revoke_token( Ok(GitHubSecretAlertFeedbackLabel::TruePositive) } -fn send_notification_email( +async fn send_notification_email( token: &ApiToken, alert: &GitHubSecretAlert, state: &AppState, - conn: &mut impl Conn, + conn: &mut AsyncPgConnection, ) -> anyhow::Result<()> { - let user = User::find(conn, token.user_id).context("Failed to find user")?; - let Some(recipient) = user.email(conn)? else { + let user = User::async_find(conn, token.user_id) + .await + .context("Failed to find user")?; + + let Some(recipient) = user.async_email(conn).await? else { return Err(anyhow!("No address found")); }; @@ -196,7 +199,7 @@ fn send_notification_email( url: &alert.url, }; - state.emails.send(&recipient, email)?; + state.emails.async_send(&recipient, email).await?; Ok(()) } @@ -268,25 +271,19 @@ pub async fn verify( let alerts: Vec = json::from_slice(&body) .map_err(|e| bad_request(format!("invalid secret alert request: {e:?}")))?; - let conn = state.db_write().await?; - spawn_blocking(move || { - let conn: &mut AsyncConnectionWrapper<_> = &mut conn.into(); - - let feedback = alerts - .into_iter() - .map(|alert| { - let label = alert_revoke_token(&state, &alert, conn)?; - Ok(GitHubSecretAlertFeedback { - token_raw: alert.token, - token_type: alert.r#type, - label, - }) - }) - .collect::>()?; - - Ok(Json(feedback)) - }) - .await + let mut conn = state.db_write().await?; + + let mut feedback = Vec::with_capacity(alerts.len()); + for alert in alerts { + let label = alert_revoke_token(&state, &alert, &mut conn).await?; + feedback.push(GitHubSecretAlertFeedback { + token_raw: alert.token, + token_type: alert.r#type, + label, + }); + } + + Ok(Json(feedback)) } #[cfg(test)]