Skip to content

Commit be10298

Browse files
committed
test(proxy): add further tests for token exchange
Signed-off-by: Gordon Sim <gsim@redhat.com>
1 parent 556de9d commit be10298

3 files changed

Lines changed: 445 additions & 44 deletions

File tree

crates/openshell-supervisor-network/src/l7/relay.rs

Lines changed: 253 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,20 +1448,7 @@ network_policies:
14481448
let config = crate::l7::parse_l7_config(&endpoint_config.unwrap()).unwrap();
14491449
let tunnel_engine = engine.clone_engine_for_tunnel(generation).unwrap();
14501450
let provider_key = "api.example.test\t8080\t/v1/**\tprovider:access_token";
1451-
let fixture = match resolver_response {
1452-
Ok(token) => {
1453-
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::success(
1454-
provider_key,
1455-
token,
1456-
)
1457-
}
1458-
Err(error) => {
1459-
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::failure(
1460-
provider_key,
1461-
error,
1462-
)
1463-
}
1464-
};
1451+
let fixture = token_grant_fixture(provider_key, resolver_response, false);
14651452
let ctx = L7EvalContext {
14661453
host: "api.example.test".into(),
14671454
port: 8080,
@@ -1478,6 +1465,24 @@ network_policies:
14781465
(config, tunnel_engine, ctx, fixture)
14791466
}
14801467

1468+
fn rest_token_exchange_relay_context(
1469+
resolver_response: std::result::Result<&str, &str>,
1470+
) -> (
1471+
L7EndpointConfig,
1472+
TunnelPolicyEngine,
1473+
L7EvalContext,
1474+
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture,
1475+
) {
1476+
let (config, tunnel_engine, mut ctx, _) =
1477+
rest_token_grant_relay_context(Ok("unused-token"));
1478+
let provider_key = "api.example.test\t8080\t/v1/**\tprovider:access_token";
1479+
let fixture = token_grant_fixture(provider_key, resolver_response, true);
1480+
ctx.dynamic_credentials = Some(fixture.dynamic_credentials());
1481+
ctx.token_grant_resolver = Some(fixture.resolver());
1482+
1483+
(config, tunnel_engine, ctx, fixture)
1484+
}
1485+
14811486
fn passthrough_token_grant_relay_context(
14821487
resolver_response: std::result::Result<&str, &str>,
14831488
) -> (
@@ -1491,20 +1496,7 @@ network_policies:
14911496
.generation_guard(engine.current_generation())
14921497
.unwrap();
14931498
let provider_key = "api.example.test\t8080\t/v1/**\tprovider:access_token";
1494-
let fixture = match resolver_response {
1495-
Ok(token) => {
1496-
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::success(
1497-
provider_key,
1498-
token,
1499-
)
1500-
}
1501-
Err(error) => {
1502-
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::failure(
1503-
provider_key,
1504-
error,
1505-
)
1506-
}
1507-
};
1499+
let fixture = token_grant_fixture(provider_key, resolver_response, false);
15081500
let ctx = L7EvalContext {
15091501
host: "api.example.test".into(),
15101502
port: 8080,
@@ -1521,6 +1513,56 @@ network_policies:
15211513
(generation_guard, ctx, fixture)
15221514
}
15231515

1516+
fn passthrough_token_exchange_relay_context(
1517+
resolver_response: std::result::Result<&str, &str>,
1518+
) -> (
1519+
PolicyGenerationGuard,
1520+
L7EvalContext,
1521+
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture,
1522+
) {
1523+
let (generation_guard, mut ctx, _) =
1524+
passthrough_token_grant_relay_context(Ok("unused-token"));
1525+
let provider_key = "api.example.test\t8080\t/v1/**\tprovider:access_token";
1526+
let fixture = token_grant_fixture(provider_key, resolver_response, true);
1527+
ctx.dynamic_credentials = Some(fixture.dynamic_credentials());
1528+
ctx.token_grant_resolver = Some(fixture.resolver());
1529+
1530+
(generation_guard, ctx, fixture)
1531+
}
1532+
1533+
fn token_grant_fixture(
1534+
provider_key: &str,
1535+
resolver_response: std::result::Result<&str, &str>,
1536+
token_exchange: bool,
1537+
) -> crate::l7::token_grant_injection::test_support::TokenGrantTestFixture {
1538+
match (resolver_response, token_exchange) {
1539+
(Ok(token), false) => {
1540+
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::success(
1541+
provider_key,
1542+
token,
1543+
)
1544+
}
1545+
(Ok(token), true) => {
1546+
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::success_token_exchange(
1547+
provider_key,
1548+
token,
1549+
)
1550+
}
1551+
(Err(error), false) => {
1552+
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::failure(
1553+
provider_key,
1554+
error,
1555+
)
1556+
}
1557+
(Err(error), true) => {
1558+
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::failure_token_exchange(
1559+
provider_key,
1560+
error,
1561+
)
1562+
}
1563+
}
1564+
}
1565+
15241566
fn authorization_header_count(headers: &str) -> usize {
15251567
headers
15261568
.lines()
@@ -1627,6 +1669,71 @@ network_policies:
16271669
fixture.assert_one_request("api.example.test\t8080\t/v1/**\tprovider:access_token");
16281670
}
16291671

1672+
#[tokio::test]
1673+
async fn l7_rest_relay_injects_token_exchange_authorization_header() {
1674+
let (config, tunnel_engine, ctx, fixture) =
1675+
rest_token_exchange_relay_context(Ok("grant-token"));
1676+
let (mut app, mut relay_client) = tokio::io::duplex(8192);
1677+
let (mut relay_upstream, mut upstream) = tokio::io::duplex(8192);
1678+
let relay = tokio::spawn(async move {
1679+
relay_with_inspection(
1680+
&config,
1681+
tunnel_engine,
1682+
&mut relay_client,
1683+
&mut relay_upstream,
1684+
&ctx,
1685+
)
1686+
.await
1687+
});
1688+
1689+
app.write_all(
1690+
b"GET /v1/projects HTTP/1.1\r\nHost: api.example.test\r\nAuthorization: Bearer stale-token\r\nConnection: close\r\n\r\n",
1691+
)
1692+
.await
1693+
.unwrap();
1694+
1695+
let mut upstream_request = [0u8; 1024];
1696+
let n = tokio::time::timeout(
1697+
std::time::Duration::from_secs(1),
1698+
upstream.read(&mut upstream_request),
1699+
)
1700+
.await
1701+
.expect("request should reach upstream")
1702+
.unwrap();
1703+
let upstream_request = String::from_utf8_lossy(&upstream_request[..n]);
1704+
1705+
assert!(upstream_request.starts_with("GET /v1/projects HTTP/1.1\r\n"));
1706+
assert!(upstream_request.contains("Authorization: Bearer grant-token\r\n"));
1707+
assert!(!upstream_request.contains("stale-token"));
1708+
assert_eq!(authorization_header_count(&upstream_request), 1);
1709+
1710+
upstream
1711+
.write_all(b"HTTP/1.1 204 No Content\r\nContent-Length: 0\r\nConnection: close\r\n\r\n")
1712+
.await
1713+
.unwrap();
1714+
1715+
let mut client_response = [0u8; 512];
1716+
let n = tokio::time::timeout(
1717+
std::time::Duration::from_secs(1),
1718+
app.read(&mut client_response),
1719+
)
1720+
.await
1721+
.expect("response should reach client")
1722+
.unwrap();
1723+
assert!(String::from_utf8_lossy(&client_response[..n]).contains("204 No Content"));
1724+
drop(app);
1725+
1726+
tokio::time::timeout(std::time::Duration::from_secs(1), relay)
1727+
.await
1728+
.expect("relay should finish")
1729+
.unwrap()
1730+
.unwrap();
1731+
1732+
fixture.assert_one_token_exchange_request(
1733+
"api.example.test\t8080\t/v1/**\tprovider:access_token",
1734+
);
1735+
}
1736+
16301737
#[tokio::test]
16311738
async fn l7_rest_relay_token_grant_failure_does_not_forward_request() {
16321739
let (config, tunnel_engine, ctx, fixture) =
@@ -1679,6 +1786,60 @@ network_policies:
16791786
fixture.assert_one_request("api.example.test\t8080\t/v1/**\tprovider:access_token");
16801787
}
16811788

1789+
#[tokio::test]
1790+
async fn l7_rest_relay_token_exchange_failure_does_not_forward_request() {
1791+
let (config, tunnel_engine, ctx, fixture) =
1792+
rest_token_exchange_relay_context(Err("oauth unavailable"));
1793+
let (mut app, mut relay_client) = tokio::io::duplex(8192);
1794+
let (mut relay_upstream, mut upstream) = tokio::io::duplex(8192);
1795+
let relay = tokio::spawn(async move {
1796+
relay_with_inspection(
1797+
&config,
1798+
tunnel_engine,
1799+
&mut relay_client,
1800+
&mut relay_upstream,
1801+
&ctx,
1802+
)
1803+
.await
1804+
});
1805+
1806+
app.write_all(
1807+
b"GET /v1/projects HTTP/1.1\r\nHost: api.example.test\r\nConnection: close\r\n\r\n",
1808+
)
1809+
.await
1810+
.unwrap();
1811+
1812+
tokio::time::timeout(std::time::Duration::from_secs(1), relay)
1813+
.await
1814+
.expect("relay should finish")
1815+
.unwrap()
1816+
.unwrap();
1817+
1818+
let mut client_response = [0u8; 512];
1819+
let n = tokio::time::timeout(
1820+
std::time::Duration::from_secs(1),
1821+
app.read(&mut client_response),
1822+
)
1823+
.await
1824+
.expect("bad gateway response should reach client")
1825+
.unwrap();
1826+
assert!(String::from_utf8_lossy(&client_response[..n]).contains("502 Bad Gateway"));
1827+
1828+
let mut upstream_request = [0u8; 128];
1829+
let n = tokio::time::timeout(
1830+
std::time::Duration::from_secs(1),
1831+
upstream.read(&mut upstream_request),
1832+
)
1833+
.await
1834+
.expect("upstream should close without forwarded data")
1835+
.unwrap();
1836+
assert_eq!(n, 0, "unauthenticated request must not reach upstream");
1837+
1838+
fixture.assert_one_token_exchange_request(
1839+
"api.example.test\t8080\t/v1/**\tprovider:access_token",
1840+
);
1841+
}
1842+
16821843
#[tokio::test]
16831844
async fn passthrough_relay_injects_token_grant_authorization_header() {
16841845
let (generation_guard, ctx, fixture) =
@@ -1741,6 +1902,70 @@ network_policies:
17411902
fixture.assert_one_request("api.example.test\t8080\t/v1/**\tprovider:access_token");
17421903
}
17431904

1905+
#[tokio::test]
1906+
async fn passthrough_relay_injects_token_exchange_authorization_header() {
1907+
let (generation_guard, ctx, fixture) =
1908+
passthrough_token_exchange_relay_context(Ok("grant-token"));
1909+
let (mut app, mut relay_client) = tokio::io::duplex(8192);
1910+
let (mut relay_upstream, mut upstream) = tokio::io::duplex(8192);
1911+
let relay = tokio::spawn(async move {
1912+
relay_passthrough_with_credentials(
1913+
&mut relay_client,
1914+
&mut relay_upstream,
1915+
&ctx,
1916+
&generation_guard,
1917+
)
1918+
.await
1919+
});
1920+
1921+
app.write_all(
1922+
b"GET /v1/projects HTTP/1.1\r\nHost: api.example.test\r\nAuthorization: Bearer stale-token\r\nConnection: close\r\n\r\n",
1923+
)
1924+
.await
1925+
.unwrap();
1926+
1927+
let mut upstream_request = [0u8; 1024];
1928+
let n = tokio::time::timeout(
1929+
std::time::Duration::from_secs(1),
1930+
upstream.read(&mut upstream_request),
1931+
)
1932+
.await
1933+
.expect("request should reach upstream")
1934+
.unwrap();
1935+
let upstream_request = String::from_utf8_lossy(&upstream_request[..n]);
1936+
1937+
assert!(upstream_request.starts_with("GET /v1/projects HTTP/1.1\r\n"));
1938+
assert!(upstream_request.contains("Authorization: Bearer grant-token\r\n"));
1939+
assert!(!upstream_request.contains("stale-token"));
1940+
assert_eq!(authorization_header_count(&upstream_request), 1);
1941+
1942+
upstream
1943+
.write_all(b"HTTP/1.1 204 No Content\r\nContent-Length: 0\r\nConnection: close\r\n\r\n")
1944+
.await
1945+
.unwrap();
1946+
1947+
let mut client_response = [0u8; 512];
1948+
let n = tokio::time::timeout(
1949+
std::time::Duration::from_secs(1),
1950+
app.read(&mut client_response),
1951+
)
1952+
.await
1953+
.expect("response should reach client")
1954+
.unwrap();
1955+
assert!(String::from_utf8_lossy(&client_response[..n]).contains("204 No Content"));
1956+
drop(app);
1957+
1958+
tokio::time::timeout(std::time::Duration::from_secs(1), relay)
1959+
.await
1960+
.expect("relay should finish")
1961+
.unwrap()
1962+
.unwrap();
1963+
1964+
fixture.assert_one_token_exchange_request(
1965+
"api.example.test\t8080\t/v1/**\tprovider:access_token",
1966+
);
1967+
}
1968+
17441969
#[tokio::test]
17451970
async fn passthrough_relay_token_grant_failure_returns_bad_gateway_without_forwarding() {
17461971
let (generation_guard, ctx, fixture) =

0 commit comments

Comments
 (0)