@@ -1266,3 +1266,182 @@ mod test {
12661266 is_send_sync_static ( o) ;
12671267 }
12681268}
1269+
1270+ #[ cfg( test) ]
1271+ #[ cfg( feature = "__rt_native__" ) ]
1272+ mod nested_fang_regression_test {
1273+ use crate :: claw:: status;
1274+ use crate :: fang:: { Context , FangAction } ;
1275+ use crate :: testing:: { Status , TestRequest , Tester } ;
1276+ use crate :: { Ohkami , Request , Response , Route } ;
1277+
1278+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
1279+ struct Principal ( & ' static str ) ;
1280+
1281+ #[ derive( Clone ) ]
1282+ struct ParentAuthFang ;
1283+
1284+ impl FangAction for ParentAuthFang {
1285+ async fn fore < ' a > ( & ' a self , req : & ' a mut Request ) -> Result < ( ) , Response > {
1286+ match req. headers . authorization ( ) {
1287+ Some ( "Bearer ops-token" ) => {
1288+ req. context . set ( Principal ( "ops-user" ) ) ;
1289+ Ok ( ( ) )
1290+ }
1291+ _ => Err ( Response :: Unauthorized ( ) ) ,
1292+ }
1293+ }
1294+ }
1295+
1296+ #[ derive( Clone ) ]
1297+ struct OpsAuthorizationFang ;
1298+
1299+ impl FangAction for OpsAuthorizationFang {
1300+ async fn fore < ' a > ( & ' a self , req : & ' a mut Request ) -> Result < ( ) , Response > {
1301+ match req. context . get :: < Principal > ( ) {
1302+ Some ( Principal ( "ops-user" ) ) => Ok ( ( ) ) ,
1303+ _ => Err ( Response :: Unauthorized ( ) ) ,
1304+ }
1305+ }
1306+ }
1307+
1308+ async fn routing_health_handler ( ) -> & ' static str {
1309+ "health"
1310+ }
1311+
1312+ async fn routing_override_set_handler ( ) -> & ' static str {
1313+ "set"
1314+ }
1315+
1316+ async fn routing_override_clear_handler ( ) -> status:: NoContent {
1317+ status:: NoContent
1318+ }
1319+
1320+ async fn metrics_handler ( ) -> & ' static str {
1321+ "metrics"
1322+ }
1323+
1324+ async fn accounting_reconciliation_handler ( ) -> & ' static str {
1325+ "reconciliation"
1326+ }
1327+
1328+ #[ test]
1329+ fn parent_context_auth_is_visible_to_nested_top_level_fang_in_realistic_order ( ) {
1330+ crate :: __rt__:: testing:: block_on ( async {
1331+ let ops_routes = Ohkami :: new ( (
1332+ OpsAuthorizationFang ,
1333+ "/routing/health" . GET ( routing_health_handler) ,
1334+ "/routing/override" . POST ( routing_override_set_handler) ,
1335+ "/routing/override" . DELETE ( routing_override_clear_handler) ,
1336+ "/metrics" . GET ( metrics_handler) ,
1337+ "/accounting/reconciliation" . GET ( accounting_reconciliation_handler) ,
1338+ ) ) ;
1339+
1340+ let protected_routes = Ohkami :: new ( ( ParentAuthFang , "/ops" . By ( ops_routes) ) ) ;
1341+
1342+ let app = Ohkami :: new ( ( Context :: new ( ( ) ) , "/api" . By ( protected_routes) ) ) ;
1343+
1344+ let tester = app. test ( ) ;
1345+
1346+ let health_res = tester
1347+ . oneshot (
1348+ TestRequest :: GET ( "/api/ops/routing/health" )
1349+ . header ( "Authorization" , "Bearer ops-token" ) ,
1350+ )
1351+ . await ;
1352+ assert_eq ! ( health_res. status( ) , Status :: OK ) ;
1353+
1354+ let set_override_res = tester
1355+ . oneshot (
1356+ TestRequest :: POST ( "/api/ops/routing/override" )
1357+ . header ( "Authorization" , "Bearer ops-token" ) ,
1358+ )
1359+ . await ;
1360+ assert_eq ! ( set_override_res. status( ) , Status :: OK ) ;
1361+
1362+ let clear_override_res = tester
1363+ . oneshot (
1364+ TestRequest :: DELETE ( "/api/ops/routing/override" )
1365+ . header ( "Authorization" , "Bearer ops-token" ) ,
1366+ )
1367+ . await ;
1368+ assert_eq ! ( clear_override_res. status( ) , Status :: NoContent ) ;
1369+
1370+ let metrics_res = tester
1371+ . oneshot (
1372+ TestRequest :: GET ( "/api/ops/metrics" )
1373+ . header ( "Authorization" , "Bearer ops-token" ) ,
1374+ )
1375+ . await ;
1376+ assert_eq ! ( metrics_res. status( ) , Status :: OK ) ;
1377+
1378+ let accounting_res = tester
1379+ . oneshot (
1380+ TestRequest :: GET ( "/api/ops/accounting/reconciliation" )
1381+ . header ( "Authorization" , "Bearer ops-token" ) ,
1382+ )
1383+ . await ;
1384+ assert_eq ! ( accounting_res. status( ) , Status :: OK ) ;
1385+ } ) ;
1386+ }
1387+
1388+ #[ test]
1389+ fn parent_context_auth_is_visible_to_nested_local_route_fangs_in_realistic_order ( ) {
1390+ crate :: __rt__:: testing:: block_on ( async {
1391+ let ops_routes = Ohkami :: new ( (
1392+ "/routing/health" . GET ( ( OpsAuthorizationFang , routing_health_handler) ) ,
1393+ "/routing/override" . POST ( ( OpsAuthorizationFang , routing_override_set_handler) ) ,
1394+ "/routing/override" . DELETE ( ( OpsAuthorizationFang , routing_override_clear_handler) ) ,
1395+ "/metrics" . GET ( ( OpsAuthorizationFang , metrics_handler) ) ,
1396+ "/accounting/reconciliation"
1397+ . GET ( ( OpsAuthorizationFang , accounting_reconciliation_handler) ) ,
1398+ ) ) ;
1399+
1400+ let protected_routes = Ohkami :: new ( ( ParentAuthFang , "/ops" . By ( ops_routes) ) ) ;
1401+
1402+ let app = Ohkami :: new ( ( Context :: new ( ( ) ) , "/api" . By ( protected_routes) ) ) ;
1403+
1404+ let tester = app. test ( ) ;
1405+
1406+ let health_res = tester
1407+ . oneshot (
1408+ TestRequest :: GET ( "/api/ops/routing/health" )
1409+ . header ( "Authorization" , "Bearer ops-token" ) ,
1410+ )
1411+ . await ;
1412+ assert_eq ! ( health_res. status( ) , Status :: OK ) ;
1413+
1414+ let set_override_res = tester
1415+ . oneshot (
1416+ TestRequest :: POST ( "/api/ops/routing/override" )
1417+ . header ( "Authorization" , "Bearer ops-token" ) ,
1418+ )
1419+ . await ;
1420+ assert_eq ! ( set_override_res. status( ) , Status :: OK ) ;
1421+
1422+ let clear_override_res = tester
1423+ . oneshot (
1424+ TestRequest :: DELETE ( "/api/ops/routing/override" )
1425+ . header ( "Authorization" , "Bearer ops-token" ) ,
1426+ )
1427+ . await ;
1428+ assert_eq ! ( clear_override_res. status( ) , Status :: NoContent ) ;
1429+
1430+ let metrics_res = tester
1431+ . oneshot (
1432+ TestRequest :: GET ( "/api/ops/metrics" )
1433+ . header ( "Authorization" , "Bearer ops-token" ) ,
1434+ )
1435+ . await ;
1436+ assert_eq ! ( metrics_res. status( ) , Status :: OK ) ;
1437+
1438+ let accounting_res = tester
1439+ . oneshot (
1440+ TestRequest :: GET ( "/api/ops/accounting/reconciliation" )
1441+ . header ( "Authorization" , "Bearer ops-token" ) ,
1442+ )
1443+ . await ;
1444+ assert_eq ! ( accounting_res. status( ) , Status :: OK ) ;
1445+ } ) ;
1446+ }
1447+ }
0 commit comments