Skip to content

Commit 3f6b2be

Browse files
committed
fix: correct STS endpoint
1 parent 758c44c commit 3f6b2be

4 files changed

Lines changed: 93 additions & 3 deletions

File tree

Cargo.lock

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

crates/core/src/router.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,22 @@ impl Default for Router {
7979
Self::new()
8080
}
8181
}
82+
83+
#[cfg(test)]
84+
mod tests {
85+
/// `matchit`'s `/{*path}` catch-all does NOT match the bare root `/`.
86+
/// Route handlers that need to match `/` must register an explicit `/` route.
87+
#[test]
88+
fn matchit_catchall_does_not_match_root() {
89+
let mut router = matchit::Router::<&str>::new();
90+
router.insert("/{*path}", "handler").unwrap();
91+
assert!(router.at("/").is_err());
92+
}
93+
94+
#[test]
95+
fn explicit_root_route_matches() {
96+
let mut router = matchit::Router::<&str>::new();
97+
router.insert("/", "root").unwrap();
98+
assert!(router.at("/").is_ok());
99+
}
100+
}

crates/sts/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ reqwest.workspace = true
2121
tracing.workspace = true
2222
quick-xml.workspace = true
2323
url.workspace = true
24+
25+
[dev-dependencies]
26+
http.workspace = true
27+
tokio = { workspace = true, features = ["rt", "macros"] }

crates/sts/src/route_handler.rs

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@ impl<C: CredentialRegistry> RouteHandler for StsHandler<C> {
2727

2828
/// Extension trait for registering STS routes on a [`Router`].
2929
pub trait StsRouterExt {
30-
/// Register a catch-all STS handler that intercepts
31-
/// `AssumeRoleWithWebIdentity` requests on any path.
30+
/// Register the STS handler on the root path (`/`).
31+
///
32+
/// STS requests are identified by query parameters
33+
/// (`Action=AssumeRoleWithWebIdentity`), not by path, and clients
34+
/// always send them to `/`.
3235
fn with_sts<C: CredentialRegistry + 'static>(
3336
self,
3437
config: C,
@@ -44,6 +47,68 @@ impl StsRouterExt for Router {
4447
cache: JwksCache,
4548
key: Option<TokenKey>,
4649
) -> Self {
47-
self.route("/{*path}", StsHandler { config, cache, key })
50+
self.route("/", StsHandler { config, cache, key })
51+
}
52+
}
53+
54+
#[cfg(test)]
55+
mod tests {
56+
use super::*;
57+
use multistore::error::ProxyError;
58+
use multistore::types::{RoleConfig, StoredCredential};
59+
60+
/// Minimal stub that satisfies `CredentialRegistry` without real data.
61+
#[derive(Clone)]
62+
struct EmptyRegistry;
63+
64+
impl CredentialRegistry for EmptyRegistry {
65+
async fn get_credential(
66+
&self,
67+
_access_key_id: &str,
68+
) -> Result<Option<StoredCredential>, ProxyError> {
69+
Ok(None)
70+
}
71+
async fn get_role(&self, _role_id: &str) -> Result<Option<RoleConfig>, ProxyError> {
72+
Ok(None)
73+
}
74+
}
75+
76+
fn test_router() -> Router {
77+
let cache = JwksCache::new(reqwest::Client::new(), std::time::Duration::from_secs(60));
78+
Router::new().with_sts(EmptyRegistry, cache, None)
79+
}
80+
81+
#[tokio::test]
82+
async fn sts_query_on_root_path_is_handled() {
83+
let router = test_router();
84+
let headers = http::HeaderMap::new();
85+
let req = RequestInfo {
86+
method: &http::Method::GET,
87+
path: "/",
88+
query: Some("Action=AssumeRoleWithWebIdentity&RoleArn=test&WebIdentityToken=tok"),
89+
headers: &headers,
90+
params: Default::default(),
91+
};
92+
assert!(
93+
router.dispatch(&req).await.is_some(),
94+
"STS request to / must be intercepted by the router"
95+
);
96+
}
97+
98+
#[tokio::test]
99+
async fn non_sts_query_on_root_path_falls_through() {
100+
let router = test_router();
101+
let headers = http::HeaderMap::new();
102+
let req = RequestInfo {
103+
method: &http::Method::GET,
104+
path: "/",
105+
query: Some("prefix=foo/"),
106+
headers: &headers,
107+
params: Default::default(),
108+
};
109+
assert!(
110+
router.dispatch(&req).await.is_none(),
111+
"non-STS request to / must fall through"
112+
);
48113
}
49114
}

0 commit comments

Comments
 (0)