Skip to content

Commit a36f87b

Browse files
committed
WIP
1 parent 03d9268 commit a36f87b

File tree

20 files changed

+1074
-57
lines changed

20 files changed

+1074
-57
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ paste = "=1.0.15"
110110
postgres-native-tls = "=0.5.1"
111111
prometheus = { version = "=0.14.0", default-features = false }
112112
rand = "=0.9.1"
113+
regex = "=1.11.1"
113114
reqwest = { version = "=0.12.15", features = ["gzip", "json"] }
114115
rss = { version = "=2.0.12", default-features = false, features = ["atom"] }
115116
secrecy = "=0.10.3"
@@ -143,11 +144,12 @@ crates_io_index = { path = "crates/crates_io_index", features = ["testing"] }
143144
crates_io_tarball = { path = "crates/crates_io_tarball", features = ["builder"] }
144145
crates_io_team_repo = { path = "crates/crates_io_team_repo", features = ["mock"] }
145146
crates_io_test_db = { path = "crates/crates_io_test_db" }
147+
crates_io_trustpub = { path = "crates/crates_io_trustpub", features = ["mock"] }
146148
claims = "=0.8.0"
147149
diesel = { version = "=2.2.10", features = ["r2d2"] }
148150
googletest = "=0.14.0"
149151
insta = { version = "=1.43.1", features = ["glob", "json", "redactions"] }
150-
regex = "=1.11.1"
152+
jsonwebtoken = "=9.3.1"
151153
sentry = { version = "=0.37.0", features = ["test"] }
152154
tokio = "=1.45.0"
153155
zip = { version = "=2.6.1", default-features = false, features = ["deflate"] }

crates/crates_io_trustpub/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ edition = "2024"
88
workspace = true
99

1010
[features]
11-
mock = ["dep:mockall"]
11+
mock = ["dep:mockall", "dep:serde_json"]
1212

1313
[dependencies]
1414
anyhow = "=1.0.98"
@@ -19,6 +19,7 @@ mockall = { version = "=0.13.1", optional = true }
1919
reqwest = { version = "=0.12.15", features = ["gzip", "json"] }
2020
regex = "=1.11.1"
2121
serde = { version = "=1.0.219", features = ["derive"] }
22+
serde_json = { version = "=1.0.140", optional = true }
2223
thiserror = "=2.0.12"
2324
tokio = { version = "=1.45.0", features = ["sync"] }
2425

crates/crates_io_trustpub/src/keystore/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,21 @@ pub trait OidcKeyStore: Send + Sync {
1818
/// is an error while fetching the key, it will return an error.
1919
async fn get_oidc_key(&self, key_id: &str) -> anyhow::Result<Option<DecodingKey>>;
2020
}
21+
22+
#[cfg(feature = "mock")]
23+
impl MockOidcKeyStore {
24+
/// Creates a new instance of [`MockOidcKeyStore`] based on the RSA keys
25+
/// provided in the [`crate::test_keys`] module.
26+
pub fn with_test_key() -> Self {
27+
use crate::test_keys::{DECODING_KEY, KEY_ID};
28+
use mockall::predicate::*;
29+
30+
let mut mock = Self::new();
31+
32+
mock.expect_get_oidc_key()
33+
.with(eq(KEY_ID))
34+
.returning(|_| Ok(Some(DECODING_KEY.clone())));
35+
36+
mock
37+
}
38+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
---
2+
source: crates/crates_io_trustpub/src/keystore/load_jwks.rs
3+
expression: jwks
4+
---
5+
JwkSet {
6+
keys: [
7+
Jwk {
8+
common: CommonParameters {
9+
public_key_use: Some(
10+
Signature,
11+
),
12+
key_operations: None,
13+
key_algorithm: Some(
14+
RS256,
15+
),
16+
key_id: Some(
17+
"cc413527-173f-5a05-976e-9c52b1d7b431",
18+
),
19+
x509_url: None,
20+
x509_chain: None,
21+
x509_sha1_fingerprint: None,
22+
x509_sha256_fingerprint: None,
23+
},
24+
algorithm: RSA(
25+
RSAKeyParameters {
26+
key_type: RSA,
27+
n: "w4M936N3ZxNaEblcUoBm-xu0-V9JxNx5S7TmF0M3SBK-2bmDyAeDdeIOTcIVZHG-ZX9N9W0u1yWafgWewHrsz66BkxXq3bscvQUTAw7W3s6TEeYY7o9shPkFfOiU3x_KYgOo06SpiFdymwJflRs9cnbaU88i5fZJmUepUHVllP2tpPWTi-7UA3AdP3cdcCs5bnFfTRKzH2W0xqKsY_jIG95aQJRBDpbiesefjuyxcQnOv88j9tCKWzHpJzRKYjAUM6OPgN4HYnaSWrPJj1v41eEkFM1kORuj-GSH2qMVD02VklcqaerhQHIqM-RjeHsN7G05YtwYzomE5G-fZuwgvQ",
28+
e: "AQAB",
29+
},
30+
),
31+
},
32+
Jwk {
33+
common: CommonParameters {
34+
public_key_use: Some(
35+
Signature,
36+
),
37+
key_operations: None,
38+
key_algorithm: Some(
39+
RS256,
40+
),
41+
key_id: Some(
42+
"38826b17-6a30-5f9b-b169-8beb8202f723",
43+
),
44+
x509_url: None,
45+
x509_chain: Some(
46+
[
47+
"MIIDKzCCAhOgAwIBAgIUDnwm6eRIqGFA3o/P1oBrChvx/nowDQYJKoZIhvcNAQELBQAwJTEjMCEGA1UEAwwaYWN0aW9ucy5zZWxmLXNpZ25lZC5naXRodWIwHhcNMjQwMTIzMTUyNTM2WhcNMzQwMTIwMTUyNTM2WjAlMSMwIQYDVQQDDBphY3Rpb25zLnNlbGYtc2lnbmVkLmdpdGh1YjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOTGp5svs8LJN8BH7VzXShWXnOK0lhDVuI0xnr5bwHFPc924CwaIEFb6mC7bvW2lZtgd633uaJ2naG6vKaOVGpCdGLE4ohH11nUk+2CNknZL7/oTmDHGSmGeHRb7kjtb0Ng4BJMPzmTYmCNUudfDFhHDcZz1Obuu85GsABrC5ZlzWzspYFXwUSaxvII+rHK/rAbOC2gmt5IOSLmgh3taQfp0mB6Lxlf89HoBPNwtPfBX8DtXTWQVnqODm4W+WfmWBSyXGX54DGNMyZwlTZqR0FjoMXxopId3MIuDGKxa2weDU5cW60N2y/qxikeV99fL3sg5aPA8s9iljKG0+MAfVNUCAwEAAaNTMFEwHQYDVR0OBBYEFIPALo5VanJ6E1B9eLQgGO+uGV65MB8GA1UdIwQYMBaAFIPALo5VanJ6E1B9eLQgGO+uGV65MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGS0hZE+DqKIRi49Z2KDOMOaSZnAYgqq6ws9HJHT09MXWlMHB8E/apvy2ZuFrcSu14ZLweJid+PrrooXEXEO6azEakzCjeUb9G1QwlzP4CkTcMGCw1Snh3jWZIuKaw21f7mp2rQ+YNltgHVDKY2s8AD273E8musEsWxJl80/MNvMie8Hfh4n4/Xl2r6t1YPmUJMoXAXdTBb0hkPy1fUu3r2T+1oi7Rw6kuVDfAZjaHupNHzJeDOg2KxUoK/GF2/M2qpVrd19Pv/JXNkQXRE4DFbErMmA7tXpp1tkXJRPhFui/Pv5H9cPgObEf9x6W4KnCXzT3ReeeRDKF8SqGTPELsc=",
48+
],
49+
),
50+
x509_sha1_fingerprint: Some(
51+
"ykNaY4qM_ta4k2TgZOCEYLkcYlA",
52+
),
53+
x509_sha256_fingerprint: None,
54+
},
55+
algorithm: RSA(
56+
RSAKeyParameters {
57+
key_type: RSA,
58+
n: "5Manmy-zwsk3wEftXNdKFZec4rSWENW4jTGevlvAcU9z3bgLBogQVvqYLtu9baVm2B3rfe5onadobq8po5UakJ0YsTiiEfXWdST7YI2Sdkvv-hOYMcZKYZ4dFvuSO1vQ2DgEkw_OZNiYI1S518MWEcNxnPU5u67zkawAGsLlmXNbOylgVfBRJrG8gj6scr-sBs4LaCa3kg5IuaCHe1pB-nSYHovGV_z0egE83C098FfwO1dNZBWeo4Obhb5Z-ZYFLJcZfngMY0zJnCVNmpHQWOgxfGikh3cwi4MYrFrbB4NTlxbrQ3bL-rGKR5X318veyDlo8Dyz2KWMobT4wB9U1Q",
59+
e: "AQAB",
60+
},
61+
),
62+
},
63+
Jwk {
64+
common: CommonParameters {
65+
public_key_use: Some(
66+
Signature,
67+
),
68+
key_operations: None,
69+
key_algorithm: Some(
70+
RS256,
71+
),
72+
key_id: Some(
73+
"1F2AB83404C08EC9EA0BB99DAED02186B091DBF4",
74+
),
75+
x509_url: None,
76+
x509_chain: Some(
77+
[
78+
"MIIDrDCCApSgAwIBAgIQAP4blP36Q3WmMOhWf0RBMzANBgkqhkiG9w0BAQsFADA2MTQwMgYDVQQDEyt2c3RzLXZzdHNnaHJ0LWdoLXZzby1vYXV0aC52aXN1YWxzdHVkaW8uY29tMB4XDTIzMTAyNDE0NTI1NVoXDTI1MTAyNDE1MDI1NVowNjE0MDIGA1UEAxMrdnN0cy12c3RzZ2hydC1naC12c28tb2F1dGgudmlzdWFsc3R1ZGlvLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALvM0mJ+SUfzucssEnjoZllnezjKC25YeIhk3iIUzlaJ/uXueESt9GEA3lAo6W/bt73R0zu10u4uhM5MC27FDoq9u7oaqBRhll0gGaz6HDqG0haCwuZdDb0ikalbaaAAzh3AIefby26/Hc98bRBBsf6pS083xX1ogiGFosteQtqKNXjT8c0Hzr3bu2Hrejn+JrrFdBLOf5jRE6XhzlRK4vD1n8c8OPOVByI97KHJeC5PyN4g8h34KU+PbSCWIRxOTSJizXcDIWtXAQiGyTMtXSQn3aCvNux4vaisgZn7TUD4XsxlUbDo7H9gX1Bsxj+aQhqxQYxDnC4Y/94/kyXm4L0CAwEAAaOBtTCBsjAOBgNVHQ8BAf8EBAMCBaAwCQYDVR0TBAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwNgYDVR0RBC8wLYIrdnN0cy12c3RzZ2hydC1naC12c28tb2F1dGgudmlzdWFsc3R1ZGlvLmNvbTAfBgNVHSMEGDAWgBSmWMP5CXuaSzoLKwcLXYZnoeCJmDAdBgNVHQ4EFgQUpljD+Ql7mks6CysHC12GZ6HgiZgwDQYJKoZIhvcNAQELBQADggEBAINwybFwYpXJkvauL5QbtrykIDYeP8oFdVIeVY8YI9MGfx7OwWDsNBVXv2B62zAZ49hK5G87++NmFI/FHnGOCISDYoJkRSCy2Nbeyr7Nx2VykWzUQqHLZfvr5KqW4Gj1OFHUqTl8lP3FWDd/P+lil3JobaSiICQshgF0GnX2a8ji8mfXpJSP20gzrLw84brmtmheAvJ9X/sLbM/RBkkT6g4NV2QbTMqo6k601qBNQBsH+lTDDWPCkRoAlW6a0z9bWIhGHWJ2lcR70zagcxIVl5/Fq35770/aMGroSrIx3JayOEqsvgIthYBKHzpT2VFwUz1VpBpNVJg9/u6jCwLY7QA=",
79+
],
80+
),
81+
x509_sha1_fingerprint: Some(
82+
"Hyq4NATAjsnqC7mdrtAhhrCR2_Q",
83+
),
84+
x509_sha256_fingerprint: None,
85+
},
86+
algorithm: RSA(
87+
RSAKeyParameters {
88+
key_type: RSA,
89+
n: "u8zSYn5JR_O5yywSeOhmWWd7OMoLblh4iGTeIhTOVon-5e54RK30YQDeUCjpb9u3vdHTO7XS7i6EzkwLbsUOir27uhqoFGGWXSAZrPocOobSFoLC5l0NvSKRqVtpoADOHcAh59vLbr8dz3xtEEGx_qlLTzfFfWiCIYWiy15C2oo1eNPxzQfOvdu7Yet6Of4musV0Es5_mNETpeHOVEri8PWfxzw485UHIj3socl4Lk_I3iDyHfgpT49tIJYhHE5NImLNdwMha1cBCIbJMy1dJCfdoK827Hi9qKyBmftNQPhezGVRsOjsf2BfUGzGP5pCGrFBjEOcLhj_3j-TJebgvQ",
90+
e: "AQAB",
91+
},
92+
),
93+
},
94+
Jwk {
95+
common: CommonParameters {
96+
public_key_use: Some(
97+
Signature,
98+
),
99+
key_operations: None,
100+
key_algorithm: Some(
101+
RS256,
102+
),
103+
key_id: Some(
104+
"001DDCD014A848E8824577B3E4F3AEDB3BCF5FFD",
105+
),
106+
x509_url: None,
107+
x509_chain: Some(
108+
[
109+
"MIIDrDCCApSgAwIBAgIQKiyRrA01T5qtxdzvZ/ErzjANBgkqhkiG9w0BAQsFADA2MTQwMgYDVQQDEyt2c3RzLXZzdHNnaHJ0LWdoLXZzby1vYXV0aC52aXN1YWxzdHVkaW8uY29tMB4XDTIzMTAxODE1MDExOFoXDTI1MTAxODE1MTExOFowNjE0MDIGA1UEAxMrdnN0cy12c3RzZ2hydC1naC12c28tb2F1dGgudmlzdWFsc3R1ZGlvLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALCP6+IjsL0cZLEqL8mTfHWubvnYdO33XKoexY8iimQjla+3WvPfMZNExvXme5XwNQpgUx+LF+Xr4OGINsXRQ8WCuMZi2XMDMkGSA4mXHSLo57bWqqZazL/7/mMgGqRxZrnWwTvDVbjh94JhmidNhvLtVXX/Atz3TStd3IeMKTb8qMcKmiFhj0s7nQOphjxqL0S4buXRDjiUibbC8xMTbzZh54TyWZNOq+xXj5AVkkNQgh1wM6QfmlFhGQtRSjEoqtnb3zgsoibbRHco/Dv+cqm9MCwD2/vDXc5KaW0WpdlhqMKFPgHo6qFiJoXw85TgUErZ4bgJbiqQOrB9InypULECAwEAAaOBtTCBsjAOBgNVHQ8BAf8EBAMCBaAwCQYDVR0TBAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwNgYDVR0RBC8wLYIrdnN0cy12c3RzZ2hydC1naC12c28tb2F1dGgudmlzdWFsc3R1ZGlvLmNvbTAfBgNVHSMEGDAWgBQ45rBfvl4JJ7vg3WgLjQTfhDihvzAdBgNVHQ4EFgQUOOawX75eCSe74N1oC40E34Q4ob8wDQYJKoZIhvcNAQELBQADggEBABdN6HPheRdzwvJgi4xGHnf9pvlUC8981kAtgHnPT0VEYXh/dCMnKJSvCDJADpdmkuKxLxAfACeZR2CUHkQ0eO1ek/ihLvPqywDhLENq6Lvzu3qlhvUPBkGYjydpLtXQ1bBXUQ1FzT5/L1U19P2rJso9mC4ltu2OHJ9NLCKG0zffBItAJqhAiXtKbCUg4c9RbQxi9T2/xr9R72di4Qygfnmr3QleAqmjRG918cm5/uJ0s5EaK3QI7GQy7+tc44o3H3AI5eFtrHwIV0zoY4A9YIsaRmMHq9soHFBEO1HDKKRUOl/4tjpx8zHpp5Clz0wiZMgvSIdBa3/fTeUJ3flUYMo=",
110+
],
111+
),
112+
x509_sha1_fingerprint: Some(
113+
"AB3c0BSoSOiCRXez5POu2zvPX_0",
114+
),
115+
x509_sha256_fingerprint: None,
116+
},
117+
algorithm: RSA(
118+
RSAKeyParameters {
119+
key_type: RSA,
120+
n: "sI_r4iOwvRxksSovyZN8da5u-dh07fdcqh7FjyKKZCOVr7da898xk0TG9eZ7lfA1CmBTH4sX5evg4Yg2xdFDxYK4xmLZcwMyQZIDiZcdIujnttaqplrMv_v-YyAapHFmudbBO8NVuOH3gmGaJ02G8u1Vdf8C3PdNK13ch4wpNvyoxwqaIWGPSzudA6mGPGovRLhu5dEOOJSJtsLzExNvNmHnhPJZk06r7FePkBWSQ1CCHXAzpB-aUWEZC1FKMSiq2dvfOCyiJttEdyj8O_5yqb0wLAPb-8NdzkppbRal2WGowoU-AejqoWImhfDzlOBQStnhuAluKpA6sH0ifKlQsQ",
121+
e: "AQAB",
122+
},
123+
),
124+
},
125+
],
126+
}

crates/crates_io_trustpub/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
pub mod github;
44
pub mod keystore;
5-
#[cfg(test)]
5+
#[cfg(any(test, feature = "mock"))]
66
pub mod test_keys;
77
pub mod unverified;
88

src/bin/server.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
#[macro_use]
22
extern crate tracing;
33

4+
use axum::ServiceExt;
45
use crates_io::middleware::normalize_path::normalize_path;
56
use crates_io::{App, Emails, metrics::LogEncoder};
6-
use std::{sync::Arc, time::Duration};
7-
8-
use axum::ServiceExt;
97
use crates_io_github::RealGitHubClient;
8+
use crates_io_trustpub::github::GITHUB_ISSUER_URL;
9+
use crates_io_trustpub::keystore::{OidcKeyStore, RealOidcKeyStore};
1010
use prometheus::Encoder;
1111
use reqwest::Client;
12+
use std::collections::HashMap;
1213
use std::io::Write;
1314
use std::net::SocketAddr;
15+
use std::{sync::Arc, time::Duration};
1416
use tokio::net::TcpListener;
1517
use tokio::signal::unix::{SignalKind, signal};
1618
use tower::Layer;
@@ -33,10 +35,15 @@ fn main() -> anyhow::Result<()> {
3335
let github = RealGitHubClient::new(client);
3436
let github = Box::new(github);
3537

38+
let github_key_store: Box<dyn OidcKeyStore> = Box::new(RealOidcKeyStore::github());
39+
let oidc_key_stores: HashMap<String, Box<dyn OidcKeyStore>> =
40+
HashMap::from([(GITHUB_ISSUER_URL.into(), github_key_store)]);
41+
3642
let app = App::builder()
3743
.databases_from_config(&config.db)
3844
.github(github)
3945
.github_oauth_from_config(&config)
46+
.oidc_key_stores(oidc_key_stores)
4047
.emails(emails)
4148
.storage_from_config(&config.storage)
4249
.rate_limiter_from_config(config.rate_limiter.clone())

0 commit comments

Comments
 (0)