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