Skip to content

Commit d1b68a4

Browse files
committed
trustpub: Add FullGitLabClaims test helper struct
1 parent 197795f commit d1b68a4

File tree

3 files changed

+189
-0
lines changed

3 files changed

+189
-0
lines changed

crates/crates_io_trustpub/src/gitlab/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
mod claims;
2+
#[cfg(any(test, feature = "test-helpers"))]
3+
pub mod test_helpers;
24
mod workflows;
35

46
pub use self::claims::GitLabClaims;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
source: crates/crates_io_trustpub/src/gitlab/test_helpers.rs
3+
expression: claims
4+
---
5+
{
6+
"iss": "https://gitlab.com",
7+
"nbf": "[timestamp]",
8+
"exp": "[timestamp]",
9+
"iat": "[timestamp]",
10+
"jti": "example-id",
11+
"sub": "project_path:octocat/hello-world:ref_type:branch:ref:main",
12+
"aud": "crates.io",
13+
"project_id": "74884433",
14+
"project_path": "octocat/hello-world",
15+
"namespace_id": "123",
16+
"namespace_path": "octocat",
17+
"user_id": "39035",
18+
"user_login": "octocat",
19+
"user_email": "[email protected]",
20+
"user_access_level": "owner",
21+
"job_project_id": "74884433",
22+
"job_project_path": "octocat/hello-world",
23+
"job_namespace_id": "123",
24+
"job_namespace_path": "octocat",
25+
"pipeline_id": "2069090987",
26+
"pipeline_source": "push",
27+
"job_id": "11530106120",
28+
"ref": "main",
29+
"ref_type": "branch",
30+
"ref_path": "refs/heads/main",
31+
"ref_protected": "true",
32+
"runner_id": 12270840,
33+
"runner_environment": "gitlab-hosted",
34+
"sha": "76719c2658b5c4423810d655a4624af1b38b7091",
35+
"project_visibility": "public",
36+
"ci_config_ref_uri": "gitlab.com/octocat/hello-world//.gitlab-ci.yml@refs/heads/main",
37+
"ci_config_sha": "76719c2658b5c4423810d655a4624af1b38b7091"
38+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use super::GITLAB_ISSUER_URL;
2+
use crate::test_keys::encode_for_testing;
3+
use bon::bon;
4+
use serde_json::json;
5+
6+
pub const AUDIENCE: &str = "crates.io";
7+
8+
/// A struct representing all the claims in a GitLab CI OIDC token.
9+
///
10+
/// This struct is used to create a JWT for testing purposes.
11+
#[derive(Debug, serde::Serialize)]
12+
pub struct FullGitLabClaims {
13+
pub iss: String,
14+
pub nbf: i64,
15+
pub exp: i64,
16+
pub iat: i64,
17+
pub jti: String,
18+
pub sub: String,
19+
pub aud: String,
20+
21+
pub project_id: String,
22+
pub project_path: String,
23+
pub namespace_id: String,
24+
pub namespace_path: String,
25+
pub user_id: String,
26+
pub user_login: String,
27+
pub user_email: String,
28+
pub user_access_level: String,
29+
pub job_project_id: String,
30+
pub job_project_path: String,
31+
pub job_namespace_id: String,
32+
pub job_namespace_path: String,
33+
pub pipeline_id: String,
34+
pub pipeline_source: String,
35+
pub job_id: String,
36+
#[serde(rename = "ref")]
37+
pub r#ref: String,
38+
pub ref_type: String,
39+
pub ref_path: String,
40+
pub ref_protected: String,
41+
#[serde(skip_serializing_if = "Option::is_none")]
42+
pub environment: Option<String>,
43+
#[serde(skip_serializing_if = "Option::is_none")]
44+
pub environment_protected: Option<String>,
45+
#[serde(skip_serializing_if = "Option::is_none")]
46+
pub deployment_tier: Option<String>,
47+
#[serde(skip_serializing_if = "Option::is_none")]
48+
pub environment_action: Option<String>,
49+
pub runner_id: i64,
50+
pub runner_environment: String,
51+
pub sha: String,
52+
pub project_visibility: String,
53+
pub ci_config_ref_uri: String,
54+
pub ci_config_sha: String,
55+
}
56+
57+
#[bon]
58+
impl FullGitLabClaims {
59+
#[builder]
60+
pub fn new(
61+
namespace_id: &str,
62+
namespace: &str,
63+
project: &str,
64+
workflow_filepath: &str,
65+
environment: Option<&str>,
66+
) -> Self {
67+
let now = chrono::Utc::now().timestamp();
68+
69+
Self {
70+
iss: GITLAB_ISSUER_URL.into(),
71+
nbf: now,
72+
iat: now,
73+
exp: now + 60 * 60,
74+
jti: "example-id".into(),
75+
sub: format!("project_path:{namespace}/{project}:ref_type:branch:ref:main"),
76+
aud: AUDIENCE.into(),
77+
78+
project_id: "74884433".into(),
79+
project_path: format!("{namespace}/{project}"),
80+
namespace_id: namespace_id.into(),
81+
namespace_path: namespace.into(),
82+
user_id: "39035".into(),
83+
user_login: namespace.into(),
84+
user_email: "[email protected]".into(),
85+
user_access_level: "owner".into(),
86+
job_project_id: "74884433".into(),
87+
job_project_path: format!("{namespace}/{project}"),
88+
job_namespace_id: namespace_id.into(),
89+
job_namespace_path: namespace.into(),
90+
pipeline_id: "2069090987".into(),
91+
pipeline_source: "push".into(),
92+
job_id: "11530106120".into(),
93+
r#ref: "main".into(),
94+
ref_type: "branch".into(),
95+
ref_path: "refs/heads/main".into(),
96+
ref_protected: "true".into(),
97+
environment: environment.map(|s| s.into()),
98+
environment_protected: environment.map(|_| "false".into()),
99+
deployment_tier: environment.map(|_| "other".into()),
100+
environment_action: environment.map(|_| "start".into()),
101+
runner_id: 12270840,
102+
runner_environment: "gitlab-hosted".into(),
103+
sha: "76719c2658b5c4423810d655a4624af1b38b7091".into(),
104+
project_visibility: "public".into(),
105+
ci_config_ref_uri: format!(
106+
"gitlab.com/{namespace}/{project}//{workflow_filepath}@refs/heads/main"
107+
),
108+
ci_config_sha: "76719c2658b5c4423810d655a4624af1b38b7091".into(),
109+
}
110+
}
111+
112+
pub fn encoded(&self) -> anyhow::Result<String> {
113+
Ok(encode_for_testing(self)?)
114+
}
115+
116+
pub fn as_exchange_body(&self) -> anyhow::Result<String> {
117+
let jwt = self.encoded()?;
118+
Ok(serde_json::to_string(&json!({ "jwt": jwt }))?)
119+
}
120+
}
121+
122+
#[cfg(test)]
123+
mod tests {
124+
use super::*;
125+
use claims::assert_ok;
126+
use insta::assert_json_snapshot;
127+
128+
#[test]
129+
fn test_gitlab_claims() {
130+
let claims = FullGitLabClaims::builder()
131+
.namespace_id("123")
132+
.namespace("octocat")
133+
.project("hello-world")
134+
.workflow_filepath(".gitlab-ci.yml")
135+
.build();
136+
137+
assert_json_snapshot!(claims, {
138+
".nbf" => "[timestamp]",
139+
".iat" => "[timestamp]",
140+
".exp" => "[timestamp]",
141+
});
142+
143+
let encoded = assert_ok!(claims.encoded());
144+
assert!(!encoded.is_empty());
145+
146+
let exchange_body = assert_ok!(claims.as_exchange_body());
147+
assert!(exchange_body.contains(&encoded));
148+
}
149+
}

0 commit comments

Comments
 (0)