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
  • Loading branch information
Turbo87 committed Dec 23, 2024
1 parent 1b86411 commit a3a7a8a
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 @@ -79,6 +80,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 @@ -116,6 +118,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 @@ -147,6 +166,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 {},
this is a confirmation email for the deletion of your \"{}\" crate.
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 @@ -185,6 +231,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 @@ -220,6 +268,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 @@ -255,6 +305,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,

this is a confirmation email for the deletion of your "foo" crate.

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,

this is a confirmation email for the deletion of your "foo" crate.

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,

this is a confirmation email for the deletion of your "foo" crate.

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 a3a7a8a

Please sign in to comment.