From 309eb6d4d9e4c2f67c914bc704e56a061f007fb9 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Wed, 24 Sep 2025 12:52:18 +0200 Subject: [PATCH 1/2] database: Add `trustpub_configs_gitlab` table --- crates/crates_io_database/src/schema.patch | 2 +- crates/crates_io_database/src/schema.rs | 24 +++++++++++++++++++ .../crates_io_database_dump/src/dump-db.toml | 12 ++++++++++ .../down.sql | 1 + .../up.sql | 20 ++++++++++++++++ 5 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 migrations/2025-09-24-104418_add_trustpub_configs_gitlab/down.sql create mode 100644 migrations/2025-09-24-104418_add_trustpub_configs_gitlab/up.sql diff --git a/crates/crates_io_database/src/schema.patch b/crates/crates_io_database/src/schema.patch index 18ce21eb9b8..076cea40cf6 100644 --- a/crates/crates_io_database/src/schema.patch +++ b/crates/crates_io_database/src/schema.patch @@ -86,8 +86,8 @@ diesel::joinable!(readme_renderings -> versions (version_id)); +diesel::joinable!(recent_crate_downloads -> crates (crate_id)); diesel::joinable!(trustpub_configs_github -> crates (crate_id)); + diesel::joinable!(trustpub_configs_gitlab -> crates (crate_id)); diesel::joinable!(version_downloads -> versions (version_id)); - diesel::joinable!(version_owner_actions -> api_tokens (api_token_id)); @@ -1140,6 +1152,7 @@ publish_limit_buckets, publish_rate_overrides, diff --git a/crates/crates_io_database/src/schema.rs b/crates/crates_io_database/src/schema.rs index 9ab4c83c010..743842a7321 100644 --- a/crates/crates_io_database/src/schema.rs +++ b/crates/crates_io_database/src/schema.rs @@ -799,6 +799,28 @@ diesel::table! { } } +diesel::table! { + /// Trusted Publisher configuration for GitLab CI + trustpub_configs_gitlab (id) { + /// Unique identifier of the `trustpub_configs_gitlab` row + id -> Int4, + /// Date and time when the configuration was created + created_at -> Timestamptz, + /// Unique identifier of the crate that this configuration is for + crate_id -> Int4, + /// GitLab namespace (user or group) that owns the project + namespace -> Varchar, + /// GitLab namespace ID, populated on first token exchange for resurrection attack protection + namespace_id -> Nullable, + /// Name of the GitLab project that this configuration is for + project -> Varchar, + /// Path to the CI/CD configuration file that will be used to publish the crate + workflow_filepath -> Varchar, + /// GitLab environment that will be used to publish the crate (if `NULL` the environment is unrestricted) + environment -> Nullable, + } +} + diesel::table! { /// Temporary access tokens for Trusted Publishing trustpub_tokens (id) { @@ -1137,6 +1159,7 @@ diesel::joinable!(publish_rate_overrides -> users (user_id)); diesel::joinable!(readme_renderings -> versions (version_id)); diesel::joinable!(recent_crate_downloads -> crates (crate_id)); diesel::joinable!(trustpub_configs_github -> crates (crate_id)); +diesel::joinable!(trustpub_configs_gitlab -> crates (crate_id)); diesel::joinable!(version_downloads -> versions (version_id)); diesel::joinable!(version_owner_actions -> api_tokens (api_token_id)); diesel::joinable!(version_owner_actions -> users (user_id)); @@ -1171,6 +1194,7 @@ diesel::allow_tables_to_appear_in_same_query!( reserved_crate_names, teams, trustpub_configs_github, + trustpub_configs_gitlab, trustpub_tokens, trustpub_used_jtis, users, diff --git a/crates/crates_io_database_dump/src/dump-db.toml b/crates/crates_io_database_dump/src/dump-db.toml index d36dd4f0229..eb1770f4888 100644 --- a/crates/crates_io_database_dump/src/dump-db.toml +++ b/crates/crates_io_database_dump/src/dump-db.toml @@ -205,6 +205,18 @@ repository_name = "private" workflow_filename = "private" environment = "private" +[trustpub_configs_gitlab] +dependencies = ["crates"] +[trustpub_configs_gitlab.columns] +id = "private" +created_at = "private" +crate_id = "private" +namespace = "private" +namespace_id = "private" +project = "private" +workflow_filepath = "private" +environment = "private" + [trustpub_tokens.columns] id = "private" created_at = "private" diff --git a/migrations/2025-09-24-104418_add_trustpub_configs_gitlab/down.sql b/migrations/2025-09-24-104418_add_trustpub_configs_gitlab/down.sql new file mode 100644 index 00000000000..f7a683ebd4e --- /dev/null +++ b/migrations/2025-09-24-104418_add_trustpub_configs_gitlab/down.sql @@ -0,0 +1 @@ +DROP TABLE trustpub_configs_gitlab; diff --git a/migrations/2025-09-24-104418_add_trustpub_configs_gitlab/up.sql b/migrations/2025-09-24-104418_add_trustpub_configs_gitlab/up.sql new file mode 100644 index 00000000000..09f61f4d4d1 --- /dev/null +++ b/migrations/2025-09-24-104418_add_trustpub_configs_gitlab/up.sql @@ -0,0 +1,20 @@ +CREATE TABLE trustpub_configs_gitlab ( + id SERIAL PRIMARY KEY, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + crate_id INTEGER NOT NULL REFERENCES crates ON DELETE CASCADE, + namespace VARCHAR NOT NULL, + namespace_id VARCHAR, + project VARCHAR NOT NULL, + workflow_filepath VARCHAR NOT NULL, + environment VARCHAR +); + +comment on table trustpub_configs_gitlab is 'Trusted Publisher configuration for GitLab CI'; +comment on column trustpub_configs_gitlab.id is 'Unique identifier of the `trustpub_configs_gitlab` row'; +comment on column trustpub_configs_gitlab.created_at is 'Date and time when the configuration was created'; +comment on column trustpub_configs_gitlab.crate_id is 'Unique identifier of the crate that this configuration is for'; +comment on column trustpub_configs_gitlab.namespace is 'GitLab namespace (user or group) that owns the project'; +comment on column trustpub_configs_gitlab.namespace_id is 'GitLab namespace ID, populated on first token exchange for resurrection attack protection'; +comment on column trustpub_configs_gitlab.project is 'Name of the GitLab project that this configuration is for'; +comment on column trustpub_configs_gitlab.workflow_filepath is 'Path to the CI/CD configuration file that will be used to publish the crate'; +comment on column trustpub_configs_gitlab.environment is 'GitLab environment that will be used to publish the crate (if `NULL` the environment is unrestricted)'; From 6e4fc5cdb9f152d8d2186118fa35c17e1da7823d Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Wed, 24 Sep 2025 13:53:07 +0200 Subject: [PATCH 2/2] database: Add `GitLabConfig` model structs --- Cargo.lock | 1 + crates/crates_io_database/Cargo.toml | 2 +- .../src/models/trustpub/gitlab_config.rs | 98 +++++++++++++++++++ .../src/models/trustpub/mod.rs | 2 + 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 crates/crates_io_database/src/models/trustpub/gitlab_config.rs diff --git a/Cargo.lock b/Cargo.lock index e3ddfb845ba..4269551fba4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3501,6 +3501,7 @@ dependencies = [ "once_cell", "pest", "pest_derive", + "regex", "serde", "similar", "walkdir", diff --git a/crates/crates_io_database/Cargo.toml b/crates/crates_io_database/Cargo.toml index 4e6fb2b5079..24681346760 100644 --- a/crates/crates_io_database/Cargo.toml +++ b/crates/crates_io_database/Cargo.toml @@ -31,5 +31,5 @@ utoipa = { version = "=5.4.0", features = ["chrono"] } claims = "=0.8.0" crates_io_test_db = { path = "../crates_io_test_db" } googletest = "=0.14.2" -insta = { version = "=1.43.2", features = ["json"] } +insta = { version = "=1.43.2", features = ["filters", "json"] } tokio = { version = "=1.47.1", features = ["macros", "rt"] } diff --git a/crates/crates_io_database/src/models/trustpub/gitlab_config.rs b/crates/crates_io_database/src/models/trustpub/gitlab_config.rs new file mode 100644 index 00000000000..f13baaef033 --- /dev/null +++ b/crates/crates_io_database/src/models/trustpub/gitlab_config.rs @@ -0,0 +1,98 @@ +use crate::schema::trustpub_configs_gitlab; +use chrono::{DateTime, Utc}; +use diesel::prelude::*; +use diesel_async::{AsyncPgConnection, RunQueryDsl}; +use serde::Serialize; + +#[derive(Debug, Identifiable, Queryable, Selectable, Serialize)] +#[diesel(table_name = trustpub_configs_gitlab, check_for_backend(diesel::pg::Pg))] +pub struct GitLabConfig { + pub id: i32, + pub created_at: DateTime, + pub crate_id: i32, + pub namespace: String, + pub namespace_id: Option, + pub project: String, + pub workflow_filepath: String, + pub environment: Option, +} + +#[derive(Debug, Insertable)] +#[diesel(table_name = trustpub_configs_gitlab, check_for_backend(diesel::pg::Pg))] +pub struct NewGitLabConfig<'a> { + pub crate_id: i32, + pub namespace: &'a str, + pub project: &'a str, + pub workflow_filepath: &'a str, + pub environment: Option<&'a str>, +} + +impl NewGitLabConfig<'_> { + pub async fn insert(&self, conn: &mut AsyncPgConnection) -> QueryResult { + self.insert_into(trustpub_configs_gitlab::table) + .returning(GitLabConfig::as_returning()) + .get_result(conn) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::models::krate::*; + use crate::schema::crates; + use crates_io_test_db::TestDatabase; + use diesel_async::RunQueryDsl; + use insta::assert_debug_snapshot; + + #[tokio::test] + async fn test_gitlab_config_insert_and_retrieve() { + let test_db = TestDatabase::new(); + let mut conn = test_db.async_connect().await; + + // Create a test crate first + let test_crate = diesel::insert_into(crates::table) + .values((crates::name.eq("test-crate"),)) + .returning(Crate::as_returning()) + .get_result(&mut conn) + .await + .unwrap(); + + // Create a new GitLab config + let new_config = NewGitLabConfig { + crate_id: test_crate.id, + namespace: "rust-lang", + project: "cargo", + workflow_filepath: ".gitlab-ci.yml", + environment: Some("production"), + }; + + // Insert the config + let inserted_config = new_config.insert(&mut conn).await.unwrap(); + + // Retrieve the config + let retrieved_config = trustpub_configs_gitlab::table + .filter(trustpub_configs_gitlab::id.eq(inserted_config.id)) + .first::(&mut conn) + .await + .unwrap(); + + // Snapshot test the structure + insta::with_settings!({ filters => vec![(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z", "[datetime]")] }, { + assert_debug_snapshot!(retrieved_config, @r#" + GitLabConfig { + id: 1, + created_at: [datetime], + crate_id: 1, + namespace: "rust-lang", + namespace_id: None, + project: "cargo", + workflow_filepath: ".gitlab-ci.yml", + environment: Some( + "production", + ), + } + "#); + }); + } +} diff --git a/crates/crates_io_database/src/models/trustpub/mod.rs b/crates/crates_io_database/src/models/trustpub/mod.rs index 5452b368d08..052d023e83e 100644 --- a/crates/crates_io_database/src/models/trustpub/mod.rs +++ b/crates/crates_io_database/src/models/trustpub/mod.rs @@ -1,9 +1,11 @@ mod data; mod github_config; +mod gitlab_config; mod token; mod used_jti; pub use self::data::TrustpubData; pub use self::github_config::{GitHubConfig, NewGitHubConfig}; +pub use self::gitlab_config::{GitLabConfig, NewGitLabConfig}; pub use self::token::NewToken; pub use self::used_jti::NewUsedJti;