Skip to content

Commit 663d0a8

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

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
@@ -1399,20 +1399,7 @@ network_policies:
13991399
let config = crate::l7::parse_l7_config(&endpoint_config.unwrap()).unwrap();
14001400
let tunnel_engine = engine.clone_engine_for_tunnel(generation).unwrap();
14011401
let provider_key = "api.example.test\t8080\t/v1/**\tprovider:access_token";
1402-
let fixture = match resolver_response {
1403-
Ok(token) => {
1404-
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::success(
1405-
provider_key,
1406-
token,
1407-
)
1408-
}
1409-
Err(error) => {
1410-
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::failure(
1411-
provider_key,
1412-
error,
1413-
)
1414-
}
1415-
};
1402+
let fixture = token_grant_fixture(provider_key, resolver_response, false);
14161403
let ctx = L7EvalContext {
14171404
host: "api.example.test".into(),
14181405
port: 8080,
@@ -1429,6 +1416,24 @@ network_policies:
14291416
(config, tunnel_engine, ctx, fixture)
14301417
}
14311418

1419+
fn rest_token_exchange_relay_context(
1420+
resolver_response: std::result::Result<&str, &str>,
1421+
) -> (
1422+
L7EndpointConfig,
1423+
TunnelPolicyEngine,
1424+
L7EvalContext,
1425+
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture,
1426+
) {
1427+
let (config, tunnel_engine, mut ctx, _) =
1428+
rest_token_grant_relay_context(Ok("unused-token"));
1429+
let provider_key = "api.example.test\t8080\t/v1/**\tprovider:access_token";
1430+
let fixture = token_grant_fixture(provider_key, resolver_response, true);
1431+
ctx.dynamic_credentials = Some(fixture.dynamic_credentials());
1432+
ctx.token_grant_resolver = Some(fixture.resolver());
1433+
1434+
(config, tunnel_engine, ctx, fixture)
1435+
}
1436+
14321437
fn passthrough_token_grant_relay_context(
14331438
resolver_response: std::result::Result<&str, &str>,
14341439
) -> (
@@ -1442,20 +1447,7 @@ network_policies:
14421447
.generation_guard(engine.current_generation())
14431448
.unwrap();
14441449
let provider_key = "api.example.test\t8080\t/v1/**\tprovider:access_token";
1445-
let fixture = match resolver_response {
1446-
Ok(token) => {
1447-
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::success(
1448-
provider_key,
1449-
token,
1450-
)
1451-
}
1452-
Err(error) => {
1453-
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::failure(
1454-
provider_key,
1455-
error,
1456-
)
1457-
}
1458-
};
1450+
let fixture = token_grant_fixture(provider_key, resolver_response, false);
14591451
let ctx = L7EvalContext {
14601452
host: "api.example.test".into(),
14611453
port: 8080,
@@ -1472,6 +1464,56 @@ network_policies:
14721464
(generation_guard, ctx, fixture)
14731465
}
14741466

1467+
fn passthrough_token_exchange_relay_context(
1468+
resolver_response: std::result::Result<&str, &str>,
1469+
) -> (
1470+
PolicyGenerationGuard,
1471+
L7EvalContext,
1472+
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture,
1473+
) {
1474+
let (generation_guard, mut ctx, _) =
1475+
passthrough_token_grant_relay_context(Ok("unused-token"));
1476+
let provider_key = "api.example.test\t8080\t/v1/**\tprovider:access_token";
1477+
let fixture = token_grant_fixture(provider_key, resolver_response, true);
1478+
ctx.dynamic_credentials = Some(fixture.dynamic_credentials());
1479+
ctx.token_grant_resolver = Some(fixture.resolver());
1480+
1481+
(generation_guard, ctx, fixture)
1482+
}
1483+
1484+
fn token_grant_fixture(
1485+
provider_key: &str,
1486+
resolver_response: std::result::Result<&str, &str>,
1487+
token_exchange: bool,
1488+
) -> crate::l7::token_grant_injection::test_support::TokenGrantTestFixture {
1489+
match (resolver_response, token_exchange) {
1490+
(Ok(token), false) => {
1491+
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::success(
1492+
provider_key,
1493+
token,
1494+
)
1495+
}
1496+
(Ok(token), true) => {
1497+
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::success_token_exchange(
1498+
provider_key,
1499+
token,
1500+
)
1501+
}
1502+
(Err(error), false) => {
1503+
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::failure(
1504+
provider_key,
1505+
error,
1506+
)
1507+
}
1508+
(Err(error), true) => {
1509+
crate::l7::token_grant_injection::test_support::TokenGrantTestFixture::failure_token_exchange(
1510+
provider_key,
1511+
error,
1512+
)
1513+
}
1514+
}
1515+
}
1516+
14751517
fn authorization_header_count(headers: &str) -> usize {
14761518
headers
14771519
.lines()
@@ -1578,6 +1620,71 @@ network_policies:
15781620
fixture.assert_one_request("api.example.test\t8080\t/v1/**\tprovider:access_token");
15791621
}
15801622

1623+
#[tokio::test]
1624+
async fn l7_rest_relay_injects_token_exchange_authorization_header() {
1625+
let (config, tunnel_engine, ctx, fixture) =
1626+
rest_token_exchange_relay_context(Ok("grant-token"));
1627+
let (mut app, mut relay_client) = tokio::io::duplex(8192);
1628+
let (mut relay_upstream, mut upstream) = tokio::io::duplex(8192);
1629+
let relay = tokio::spawn(async move {
1630+
relay_with_inspection(
1631+
&config,
1632+
tunnel_engine,
1633+
&mut relay_client,
1634+
&mut relay_upstream,
1635+
&ctx,
1636+
)
1637+
.await
1638+
});
1639+
1640+
app.write_all(
1641+
b"GET /v1/projects HTTP/1.1\r\nHost: api.example.test\r\nAuthorization: Bearer stale-token\r\nConnection: close\r\n\r\n",
1642+
)
1643+
.await
1644+
.unwrap();
1645+
1646+
let mut upstream_request = [0u8; 1024];
1647+
let n = tokio::time::timeout(
1648+
std::time::Duration::from_secs(1),
1649+
upstream.read(&mut upstream_request),
1650+
)
1651+
.await
1652+
.expect("request should reach upstream")
1653+
.unwrap();
1654+
let upstream_request = String::from_utf8_lossy(&upstream_request[..n]);
1655+
1656+
assert!(upstream_request.starts_with("GET /v1/projects HTTP/1.1\r\n"));
1657+
assert!(upstream_request.contains("Authorization: Bearer grant-token\r\n"));
1658+
assert!(!upstream_request.contains("stale-token"));
1659+
assert_eq!(authorization_header_count(&upstream_request), 1);
1660+
1661+
upstream
1662+
.write_all(b"HTTP/1.1 204 No Content\r\nContent-Length: 0\r\nConnection: close\r\n\r\n")
1663+
.await
1664+
.unwrap();
1665+
1666+
let mut client_response = [0u8; 512];
1667+
let n = tokio::time::timeout(
1668+
std::time::Duration::from_secs(1),
1669+
app.read(&mut client_response),
1670+
)
1671+
.await
1672+
.expect("response should reach client")
1673+
.unwrap();
1674+
assert!(String::from_utf8_lossy(&client_response[..n]).contains("204 No Content"));
1675+
drop(app);
1676+
1677+
tokio::time::timeout(std::time::Duration::from_secs(1), relay)
1678+
.await
1679+
.expect("relay should finish")
1680+
.unwrap()
1681+
.unwrap();
1682+
1683+
fixture.assert_one_token_exchange_request(
1684+
"api.example.test\t8080\t/v1/**\tprovider:access_token",
1685+
);
1686+
}
1687+
15811688
#[tokio::test]
15821689
async fn l7_rest_relay_token_grant_failure_does_not_forward_request() {
15831690
let (config, tunnel_engine, ctx, fixture) =
@@ -1630,6 +1737,60 @@ network_policies:
16301737
fixture.assert_one_request("api.example.test\t8080\t/v1/**\tprovider:access_token");
16311738
}
16321739

1740+
#[tokio::test]
1741+
async fn l7_rest_relay_token_exchange_failure_does_not_forward_request() {
1742+
let (config, tunnel_engine, ctx, fixture) =
1743+
rest_token_exchange_relay_context(Err("oauth unavailable"));
1744+
let (mut app, mut relay_client) = tokio::io::duplex(8192);
1745+
let (mut relay_upstream, mut upstream) = tokio::io::duplex(8192);
1746+
let relay = tokio::spawn(async move {
1747+
relay_with_inspection(
1748+
&config,
1749+
tunnel_engine,
1750+
&mut relay_client,
1751+
&mut relay_upstream,
1752+
&ctx,
1753+
)
1754+
.await
1755+
});
1756+
1757+
app.write_all(
1758+
b"GET /v1/projects HTTP/1.1\r\nHost: api.example.test\r\nConnection: close\r\n\r\n",
1759+
)
1760+
.await
1761+
.unwrap();
1762+
1763+
tokio::time::timeout(std::time::Duration::from_secs(1), relay)
1764+
.await
1765+
.expect("relay should finish")
1766+
.unwrap()
1767+
.unwrap();
1768+
1769+
let mut client_response = [0u8; 512];
1770+
let n = tokio::time::timeout(
1771+
std::time::Duration::from_secs(1),
1772+
app.read(&mut client_response),
1773+
)
1774+
.await
1775+
.expect("bad gateway response should reach client")
1776+
.unwrap();
1777+
assert!(String::from_utf8_lossy(&client_response[..n]).contains("502 Bad Gateway"));
1778+
1779+
let mut upstream_request = [0u8; 128];
1780+
let n = tokio::time::timeout(
1781+
std::time::Duration::from_secs(1),
1782+
upstream.read(&mut upstream_request),
1783+
)
1784+
.await
1785+
.expect("upstream should close without forwarded data")
1786+
.unwrap();
1787+
assert_eq!(n, 0, "unauthenticated request must not reach upstream");
1788+
1789+
fixture.assert_one_token_exchange_request(
1790+
"api.example.test\t8080\t/v1/**\tprovider:access_token",
1791+
);
1792+
}
1793+
16331794
#[tokio::test]
16341795
async fn passthrough_relay_injects_token_grant_authorization_header() {
16351796
let (generation_guard, ctx, fixture) =
@@ -1692,6 +1853,70 @@ network_policies:
16921853
fixture.assert_one_request("api.example.test\t8080\t/v1/**\tprovider:access_token");
16931854
}
16941855

1856+
#[tokio::test]
1857+
async fn passthrough_relay_injects_token_exchange_authorization_header() {
1858+
let (generation_guard, ctx, fixture) =
1859+
passthrough_token_exchange_relay_context(Ok("grant-token"));
1860+
let (mut app, mut relay_client) = tokio::io::duplex(8192);
1861+
let (mut relay_upstream, mut upstream) = tokio::io::duplex(8192);
1862+
let relay = tokio::spawn(async move {
1863+
relay_passthrough_with_credentials(
1864+
&mut relay_client,
1865+
&mut relay_upstream,
1866+
&ctx,
1867+
&generation_guard,
1868+
)
1869+
.await
1870+
});
1871+
1872+
app.write_all(
1873+
b"GET /v1/projects HTTP/1.1\r\nHost: api.example.test\r\nAuthorization: Bearer stale-token\r\nConnection: close\r\n\r\n",
1874+
)
1875+
.await
1876+
.unwrap();
1877+
1878+
let mut upstream_request = [0u8; 1024];
1879+
let n = tokio::time::timeout(
1880+
std::time::Duration::from_secs(1),
1881+
upstream.read(&mut upstream_request),
1882+
)
1883+
.await
1884+
.expect("request should reach upstream")
1885+
.unwrap();
1886+
let upstream_request = String::from_utf8_lossy(&upstream_request[..n]);
1887+
1888+
assert!(upstream_request.starts_with("GET /v1/projects HTTP/1.1\r\n"));
1889+
assert!(upstream_request.contains("Authorization: Bearer grant-token\r\n"));
1890+
assert!(!upstream_request.contains("stale-token"));
1891+
assert_eq!(authorization_header_count(&upstream_request), 1);
1892+
1893+
upstream
1894+
.write_all(b"HTTP/1.1 204 No Content\r\nContent-Length: 0\r\nConnection: close\r\n\r\n")
1895+
.await
1896+
.unwrap();
1897+
1898+
let mut client_response = [0u8; 512];
1899+
let n = tokio::time::timeout(
1900+
std::time::Duration::from_secs(1),
1901+
app.read(&mut client_response),
1902+
)
1903+
.await
1904+
.expect("response should reach client")
1905+
.unwrap();
1906+
assert!(String::from_utf8_lossy(&client_response[..n]).contains("204 No Content"));
1907+
drop(app);
1908+
1909+
tokio::time::timeout(std::time::Duration::from_secs(1), relay)
1910+
.await
1911+
.expect("relay should finish")
1912+
.unwrap()
1913+
.unwrap();
1914+
1915+
fixture.assert_one_token_exchange_request(
1916+
"api.example.test\t8080\t/v1/**\tprovider:access_token",
1917+
);
1918+
}
1919+
16951920
#[tokio::test]
16961921
async fn passthrough_relay_token_grant_failure_returns_bad_gateway_without_forwarding() {
16971922
let (generation_guard, ctx, fixture) =

0 commit comments

Comments
 (0)