From f10211ebbfaae22a40e63a1a7d2689d283b84931 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 12 Jun 2025 19:48:49 +0200 Subject: [PATCH] trustpub: Disallow config creation if email is not verified --- .../trustpub/github_configs/create/mod.rs | 12 ++++-- .../trustpub/github_configs/create/tests.rs | 37 ++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/controllers/trustpub/github_configs/create/mod.rs b/src/controllers/trustpub/github_configs/create/mod.rs index 8aff9f0f062..d7fffb6c1c3 100644 --- a/src/controllers/trustpub/github_configs/create/mod.rs +++ b/src/controllers/trustpub/github_configs/create/mod.rs @@ -3,7 +3,7 @@ use crate::auth::AuthCheck; use crate::controllers::krate::load_crate; use crate::controllers::trustpub::github_configs::emails::ConfigCreatedEmail; use crate::controllers::trustpub::github_configs::json; -use crate::util::errors::{AppResult, bad_request}; +use crate::util::errors::{AppResult, bad_request, forbidden}; use axum::Json; use crates_io_database::models::OwnerKind; use crates_io_database::models::trustpub::NewGitHubConfig; @@ -61,8 +61,14 @@ pub async fn create_trustpub_github_config( .load::<(i32, String, String, bool)>(&mut conn) .await?; - if !user_owners.iter().any(|owner| owner.0 == auth_user.id) { - return Err(bad_request("You are not an owner of this crate")); + let (_, _, _, email_verified) = user_owners + .iter() + .find(|(id, _, _, _)| *id == auth_user.id) + .ok_or_else(|| bad_request("You are not an owner of this crate"))?; + + if !email_verified { + let message = "You must verify your email address to create a Trusted Publishing config"; + return Err(forbidden(message)); } // Lookup `repository_owner_id` via GitHub API diff --git a/src/controllers/trustpub/github_configs/create/tests.rs b/src/controllers/trustpub/github_configs/create/tests.rs index 9905d03375c..9cdc91bfa11 100644 --- a/src/controllers/trustpub/github_configs/create/tests.rs +++ b/src/controllers/trustpub/github_configs/create/tests.rs @@ -2,7 +2,7 @@ use crate::tests::builders::CrateBuilder; use crate::tests::util::{RequestHelper, Response, TestApp}; use anyhow::anyhow; use bytes::Bytes; -use crates_io_database::schema::trustpub_configs_github; +use crates_io_database::schema::{emails, trustpub_configs_github}; use crates_io_github::{GitHubError, GitHubUser, MockGitHubClient}; use diesel::prelude::*; use diesel_async::RunQueryDsl; @@ -370,3 +370,38 @@ async fn test_github_error() -> anyhow::Result<()> { Ok(()) } + +#[tokio::test(flavor = "multi_thread")] +async fn test_unverified_email() -> anyhow::Result<()> { + let (app, _client, cookie_client) = TestApp::full() + .with_github(simple_github_mock()) + .with_user() + .await; + + let mut conn = app.db_conn().await; + + diesel::update(emails::table.filter(emails::user_id.eq(cookie_client.as_model().id))) + .set(emails::verified.eq(false)) + .execute(&mut conn) + .await?; + + CrateBuilder::new(CRATE_NAME, cookie_client.as_model().id) + .build(&mut conn) + .await?; + + let body = serde_json::to_vec(&json!({ + "github_config": { + "crate": CRATE_NAME, + "repository_owner": "rust-lang", + "repository_name": "foo-rs", + "workflow_filename": "publish.yml", + "environment": null, + } + }))?; + + let response = cookie_client.put::<()>(URL, body).await; + assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"You must verify your email address to create a Trusted Publishing config"}]}"#); + + Ok(()) +}