@@ -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\t 8080\t /v1/**\t provider: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\t 8080\t /v1/**\t provider: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\t 8080\t /v1/**\t provider: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\t 8080\t /v1/**\t provider: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\t 8080\t /v1/**\t provider: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 \n Host: api.example.test\r \n Authorization: Bearer stale-token\r \n Connection: 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 \n Content-Length: 0\r \n Connection: 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\t 8080\t /v1/**\t provider: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\t 8080\t /v1/**\t provider: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 \n Host: api.example.test\r \n Connection: 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\t 8080\t /v1/**\t provider: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\t 8080\t /v1/**\t provider: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 \n Host: api.example.test\r \n Authorization: Bearer stale-token\r \n Connection: 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 \n Content-Length: 0\r \n Connection: 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\t 8080\t /v1/**\t provider: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