Skip to content

Commit

Permalink
controllers/krate/delete: Send deletion notification email after succ…
Browse files Browse the repository at this point in the history
…essful deletion (#10265)
  • Loading branch information
Turbo87 authored Dec 30, 2024
1 parent ef31905 commit 68064d3
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 0 deletions.
52 changes: 52 additions & 0 deletions src/controllers/krate/delete.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::app::AppState;
use crate::auth::AuthCheck;
use crate::controllers::krate::CratePath;
use crate::email::Email;
use crate::models::{NewDeletedCrate, Rights};
use crate::schema::{crate_downloads, crates, dependencies};
use crate::util::errors::{custom, AppResult, BoxedAppError};
Expand Down Expand Up @@ -80,6 +81,7 @@ pub async fn delete_crate(path: CratePath, parts: Parts, app: AppState) -> AppRe
}
}

let crate_name = krate.name.clone();
conn.transaction(|conn| {
async move {
diesel::delete(crates::table.find(krate.id))
Expand Down Expand Up @@ -117,6 +119,23 @@ pub async fn delete_crate(path: CratePath, parts: Parts, app: AppState) -> AppRe
})
.await?;

let email_future = async {
if let Some(recipient) = user.email(&mut conn).await? {
let email = CrateDeletionEmail {
user: &user.gh_login,
krate: &crate_name,
};

app.emails.send(&recipient, email).await?
}

Ok::<_, anyhow::Error>(())
};

if let Err(err) = email_future.await {
error!("Failed to send crate deletion email: {err}");
}

Ok(StatusCode::NO_CONTENT)
}

Expand Down Expand Up @@ -148,6 +167,33 @@ async fn has_rev_dep(conn: &mut AsyncPgConnection, crate_id: i32) -> QueryResult
Ok(rev_dep.is_some())
}

/// Email template for notifying a crate owner about a crate being deleted.
///
/// The owner usually should be aware of the deletion since they initiated it,
/// but this email can be helpful in detecting malicious account activity.
#[derive(Debug, Clone)]
struct CrateDeletionEmail<'a> {
user: &'a str,
krate: &'a str,
}

impl Email for CrateDeletionEmail<'_> {
fn subject(&self) -> String {
format!("crates.io: Deleted \"{}\" crate", self.krate)
}

fn body(&self) -> String {
format!(
"Hi {},
your \"{}\" crate has been deleted, per your request.
If you did not initiate this deletion, your account may have been compromised. Please contact us at [email protected].",
self.user, self.krate
)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -186,6 +232,8 @@ mod tests {
assert_eq!(response.status(), StatusCode::NO_CONTENT);
assert!(response.body().is_empty());

assert_snapshot!(app.emails_snapshot().await);

// Assert that the crate no longer exists
assert_crate_exists(&anon, "foo", false).await;
assert!(!upstream.crate_exists("foo")?);
Expand Down Expand Up @@ -221,6 +269,8 @@ mod tests {
assert_eq!(response.status(), StatusCode::NO_CONTENT);
assert!(response.body().is_empty());

assert_snapshot!(app.emails_snapshot().await);

// Assert that the crate no longer exists
assert_crate_exists(&anon, "foo", false).await;
assert!(!upstream.crate_exists("foo")?);
Expand Down Expand Up @@ -256,6 +306,8 @@ mod tests {
assert_eq!(response.status(), StatusCode::NO_CONTENT);
assert!(response.body().is_empty());

assert_snapshot!(app.emails_snapshot().await);

// Assert that the crate no longer exists
assert_crate_exists(&anon, "foo", false).await;
assert!(!upstream.crate_exists("foo")?);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
source: src/controllers/krate/delete.rs
expression: app.emails_snapshot().await
snapshot_kind: text
---
To: foo@example.com
From: crates.io <noreply@crates.io>
Subject: crates.io: Successfully published foo@1.0.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Hello foo!

A new version of the package foo (1.0.0) was published by your account (htt=
ps://crates.io/users/foo) at [0000-00-00T00:00:00Z].

If you have questions or security concerns, you can contact us at help@crat=
es.io. If you would like to stop receiving these security notifications, yo=
u can disable them in your account settings.
----------------------------------------

To: foo@example.com
From: crates.io <noreply@crates.io>
Subject: crates.io: Deleted "foo" crate
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Hi foo,

your "foo" crate has been deleted, per your request.

If you did not initiate this deletion, your account may have been compromis=
ed. Please contact us at help@crates.io.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
source: src/controllers/krate/delete.rs
expression: app.emails_snapshot().await
snapshot_kind: text
---
To: foo@example.com
From: crates.io <noreply@crates.io>
Subject: crates.io: Successfully published foo@1.0.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Hello foo!

A new version of the package foo (1.0.0) was published by your account (htt=
ps://crates.io/users/foo) at [0000-00-00T00:00:00Z].

If you have questions or security concerns, you can contact us at help@crat=
es.io. If you would like to stop receiving these security notifications, yo=
u can disable them in your account settings.
----------------------------------------

To: foo@example.com
From: crates.io <noreply@crates.io>
Subject: crates.io: Deleted "foo" crate
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Hi foo,

your "foo" crate has been deleted, per your request.

If you did not initiate this deletion, your account may have been compromis=
ed. Please contact us at help@crates.io.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
source: src/controllers/krate/delete.rs
expression: app.emails_snapshot().await
snapshot_kind: text
---
To: foo@example.com
From: crates.io <noreply@crates.io>
Subject: crates.io: Successfully published foo@1.0.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Hello foo!

A new version of the package foo (1.0.0) was published by your account (htt=
ps://crates.io/users/foo) at [0000-00-00T00:00:00Z].

If you have questions or security concerns, you can contact us at help@crat=
es.io. If you would like to stop receiving these security notifications, yo=
u can disable them in your account settings.
----------------------------------------

To: foo@example.com
From: crates.io <noreply@crates.io>
Subject: crates.io: Deleted "foo" crate
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Hi foo,

your "foo" crate has been deleted, per your request.

If you did not initiate this deletion, your account may have been compromis=
ed. Please contact us at help@crates.io.

0 comments on commit 68064d3

Please sign in to comment.