@@ -106,7 +106,7 @@ pub enum TlsMode {
106106#[ derive( Clone ) ]
107107pub struct WsState {
108108 frontegg : Arc < Option < FronteggAuthentication > > ,
109- adapter_client : mz_adapter:: Client ,
109+ adapter_client_rx : Delayed < mz_adapter:: Client > ,
110110 active_connection_count : SharedConnectionCounter ,
111111}
112112
@@ -135,13 +135,13 @@ impl HttpServer {
135135 adapter_client_tx
136136 . send ( adapter_client. clone ( ) )
137137 . expect ( "rx known to be live" ) ;
138-
138+ let adapter_client_rx = adapter_client_rx . shared ( ) ;
139139 let base_router = base_router ( BaseRouterConfig { profiling : false } )
140140 . layer ( middleware:: from_fn ( move |req, next| {
141141 let base_frontegg = Arc :: clone ( & base_frontegg) ;
142- async move { http_auth ( req, next, tls_mode, & base_frontegg) . await }
142+ async move { http_auth ( req, next, tls_mode, base_frontegg. as_ref ( ) . as_ref ( ) ) . await }
143143 } ) )
144- . layer ( Extension ( adapter_client_rx. shared ( ) ) )
144+ . layer ( Extension ( adapter_client_rx. clone ( ) ) )
145145 . layer ( Extension ( Arc :: clone ( & active_connection_count) ) )
146146 . layer (
147147 CorsLayer :: new ( )
@@ -156,12 +156,11 @@ impl HttpServer {
156156 . expose_headers ( Any )
157157 . max_age ( Duration :: from_secs ( 60 ) * 60 ) ,
158158 ) ;
159-
160159 let ws_router = Router :: new ( )
161160 . route ( "/api/experimental/sql" , routing:: get ( sql:: handle_sql_ws) )
162161 . with_state ( WsState {
163162 frontegg,
164- adapter_client : adapter_client . clone ( ) ,
163+ adapter_client_rx ,
165164 active_connection_count,
166165 } ) ;
167166
@@ -375,6 +374,7 @@ impl InternalHttpServer {
375374 "/internal-console" . to_string ( ) ,
376375 ) ) ;
377376
377+ let adapter_client_rx = adapter_client_rx. shared ( ) ;
378378 let router = base_router ( BaseRouterConfig { profiling : true } )
379379 . route (
380380 "/metrics" ,
@@ -439,9 +439,22 @@ impl InternalHttpServer {
439439 routing:: get ( console:: handle_internal_console) ,
440440 )
441441 . layer ( middleware:: from_fn ( internal_http_auth) )
442- . layer ( Extension ( adapter_client_rx. shared ( ) ) )
442+ . layer ( Extension ( adapter_client_rx. clone ( ) ) )
443443 . layer ( Extension ( console_config) )
444- . layer ( Extension ( active_connection_count) ) ;
444+ . layer ( Extension ( Arc :: clone ( & active_connection_count) ) ) ;
445+
446+ let ws_router = Router :: new ( )
447+ . route ( "/api/experimental/sql" , routing:: get ( sql:: handle_sql_ws) )
448+ // This middleware extracts the MZ user from the x-materialize-user http header.
449+ // Normally, browser-initiated websocket requests do not support headers, however for the
450+ // Internal HTTP Server the browser would be connecting through teleport, which should
451+ // attach the x-materialize-user header to all requests it proxies to this api.
452+ . layer ( middleware:: from_fn ( internal_http_auth) )
453+ . with_state ( WsState {
454+ frontegg : Arc :: new ( None ) ,
455+ adapter_client_rx,
456+ active_connection_count,
457+ } ) ;
445458
446459 let leader_router = Router :: new ( )
447460 . route ( "/api/leader/status" , routing:: get ( handle_leader_status) )
@@ -451,7 +464,10 @@ impl InternalHttpServer {
451464 ready_to_promote,
452465 } ) ) ) ;
453466
454- let router = router. merge ( leader_router) . apply_default_layers ( metrics) ;
467+ let router = router
468+ . merge ( ws_router)
469+ . merge ( leader_router)
470+ . apply_default_layers ( metrics) ;
455471
456472 InternalHttpServer { router }
457473 }
@@ -484,7 +500,10 @@ impl Server for InternalHttpServer {
484500 let router = self . router . clone ( ) ;
485501 Box :: pin ( async {
486502 let http = hyper:: server:: conn:: Http :: new ( ) ;
487- http. serve_connection ( conn, router) . err_into ( ) . await
503+ http. serve_connection ( conn, router)
504+ . with_upgrades ( )
505+ . err_into ( )
506+ . await
488507 } )
489508 }
490509}
@@ -500,7 +519,7 @@ enum ConnProtocol {
500519}
501520
502521#[ derive( Clone , Debug ) ]
503- struct AuthedUser ( User ) ;
522+ pub struct AuthedUser ( User ) ;
504523
505524pub struct AuthedClient {
506525 pub client : SessionClient ,
@@ -642,7 +661,7 @@ async fn http_auth<B>(
642661 mut req : Request < B > ,
643662 next : Next < B > ,
644663 tls_mode : TlsMode ,
645- frontegg : & Option < FronteggAuthentication > ,
664+ frontegg : Option < & FronteggAuthentication > ,
646665) -> impl IntoResponse {
647666 // First, extract the username from the certificate, validating that the
648667 // connection matches the TLS configuration along the way.
@@ -685,9 +704,10 @@ async fn http_auth<B>(
685704async fn init_ws (
686705 WsState {
687706 frontegg,
688- adapter_client ,
707+ adapter_client_rx ,
689708 active_connection_count,
690709 } : & WsState ,
710+ existing_user : Option < AuthedUser > ,
691711 ws : & mut WebSocket ,
692712) -> Result < AuthedClient , anyhow:: Error > {
693713 // TODO: Add a timeout here to prevent resource leaks by clients that
@@ -709,33 +729,59 @@ async fn init_ws(
709729 }
710730 }
711731 } ;
712- let ( creds, options) = if frontegg. is_some ( ) {
713- match ws_auth {
732+ let ( user, options) = match ( frontegg. as_ref ( ) , existing_user, ws_auth) {
733+ ( Some ( frontegg) , None , ws_auth) => {
734+ let ( creds, options) = match ws_auth {
735+ WebSocketAuth :: Basic {
736+ user,
737+ password,
738+ options,
739+ } => {
740+ let creds = Credentials :: Password {
741+ username : user,
742+ password,
743+ } ;
744+ ( creds, options)
745+ }
746+ WebSocketAuth :: Bearer { token, options } => {
747+ let creds = Credentials :: Token { token } ;
748+ ( creds, options)
749+ }
750+ WebSocketAuth :: OptionsOnly { options : _ } => {
751+ anyhow:: bail!( "expected auth information" ) ;
752+ }
753+ } ;
754+ ( auth ( Some ( frontegg) , creds) . await ?, options)
755+ }
756+ (
757+ None ,
758+ None ,
714759 WebSocketAuth :: Basic {
715760 user,
716- password,
761+ password : _ ,
717762 options,
718- } => {
719- let creds = Credentials :: Password {
720- username : user,
721- password,
722- } ;
723- ( creds, options)
724- }
725- WebSocketAuth :: Bearer { token, options } => {
726- let creds = Credentials :: Token { token } ;
727- ( creds, options)
728- }
763+ } ,
764+ ) => ( auth ( None , Credentials :: User ( user) ) . await ?, options) ,
765+ // No frontegg, specified existing user, we only accept options only.
766+ ( None , Some ( existing_user) , WebSocketAuth :: OptionsOnly { options } ) => {
767+ ( existing_user, options)
768+ }
769+ // No frontegg, specified existing user, we do not expect basic or bearer auth.
770+ ( None , Some ( _) , WebSocketAuth :: Basic { .. } | WebSocketAuth :: Bearer { .. } ) => {
771+ warn ! ( "Unexpected bearer or basic auth provided when using user header" ) ;
772+ anyhow:: bail!( "unexpected" )
773+ }
774+ // Specifying both frontegg and an existing user should not be possible.
775+ ( Some ( _) , Some ( _) , _) => anyhow:: bail!( "unexpected" ) ,
776+ // No frontegg, no existing user, and no passed username.
777+ ( None , None , WebSocketAuth :: Bearer { .. } | WebSocketAuth :: OptionsOnly { .. } ) => {
778+ warn ! ( "Unexpected auth type when not using frontegg or user header" ) ;
779+ anyhow:: bail!( "unexpected" )
729780 }
730- } else if let WebSocketAuth :: Basic { user, options, .. } = ws_auth {
731- ( Credentials :: User ( user) , options)
732- } else {
733- anyhow:: bail!( "unexpected" )
734781 } ;
735- let user = auth ( frontegg, creds) . await ?;
736782
737783 let client = AuthedClient :: new (
738- adapter_client ,
784+ & adapter_client_rx . clone ( ) . await ? ,
739785 user,
740786 Arc :: clone ( active_connection_count) ,
741787 options,
@@ -753,7 +799,7 @@ enum Credentials {
753799}
754800
755801async fn auth (
756- frontegg : & Option < FronteggAuthentication > ,
802+ frontegg : Option < & FronteggAuthentication > ,
757803 creds : Credentials ,
758804) -> Result < AuthedUser , AuthError > {
759805 // There are three places a username may be specified:
0 commit comments