@@ -44,8 +44,9 @@ use actix_web::{
4444 error:: Error ,
4545 get,
4646 http:: header:: { ContentType , DispositionType } ,
47- web,
47+ middleware , web,
4848} ;
49+ use actix_web_httpauth:: { extractors:: basic:: BasicAuth , middleware:: HttpAuthentication } ;
4950use actix_ws:: AggregatedMessage ;
5051use bytes:: Bytes ;
5152use dunce:: simplified;
@@ -284,23 +285,31 @@ struct UpdateMessageContents {
284285/// Define the [state](https://actix.rs/docs/application/#state) available to
285286/// all endpoints.
286287pub struct AppState {
287- // Provide methods to control the server.
288+ /// Provide methods to control the server.
288289 server_handle : Mutex < Option < ServerHandle > > ,
289- // The number of the next connection ID to assign.
290+ /// The number of the next connection ID to assign.
290291 connection_id : Mutex < u32 > ,
291- // The port this server listens on.
292+ /// The port this server listens on.
292293 port : u16 ,
293- // For each connection ID, store a queue tx for the HTTP server to send
294- // requests to the processing task for that ID.
294+ /// For each connection ID, store a queue tx for the HTTP server to send
295+ /// requests to the processing task for that ID.
295296 processing_task_queue_tx : Arc < Mutex < HashMap < String , Sender < ProcessingTaskHttpRequest > > > > ,
296- // For each (connection ID, requested URL) store channel to send the
297- // matching response to the HTTP task.
297+ /// For each (connection ID, requested URL) store channel to send the
298+ /// matching response to the HTTP task.
298299 filewatcher_client_queues : Arc < Mutex < HashMap < String , WebsocketQueues > > > ,
299- // For each connection ID, store the queues for the VSCode IDE.
300+ /// For each connection ID, store the queues for the VSCode IDE.
300301 vscode_ide_queues : Arc < Mutex < HashMap < String , WebsocketQueues > > > ,
301302 vscode_client_queues : Arc < Mutex < HashMap < String , WebsocketQueues > > > ,
302- // Connection IDs that are currently in use.
303+ /// Connection IDs that are currently in use.
303304 vscode_connection_id : Arc < Mutex < HashSet < String > > > ,
305+ /// The auth credentials if authentication is used.
306+ credentials : Option < Credentials > ,
307+ }
308+
309+ #[ derive( Clone ) ]
310+ pub struct Credentials {
311+ pub username : String ,
312+ pub password : String ,
304313}
305314
306315// Macros
@@ -1333,32 +1342,69 @@ async fn client_websocket(
13331342// Webserver core
13341343// --------------
13351344#[ actix_web:: main]
1336- pub async fn main ( addr : & SocketAddr ) -> std:: io:: Result < ( ) > {
1337- run_server ( addr) . await
1345+ pub async fn main ( addr : & SocketAddr , credentials : Option < Credentials > ) -> std:: io:: Result < ( ) > {
1346+ run_server ( addr, credentials ) . await
13381347}
13391348
1340- pub async fn run_server ( addr : & SocketAddr ) -> std:: io:: Result < ( ) > {
1349+ pub async fn run_server (
1350+ addr : & SocketAddr ,
1351+ credentials : Option < Credentials > ,
1352+ ) -> std:: io:: Result < ( ) > {
13411353 // Connect to the Capture Database
13421354 //let _event_capture = EventCapture::new("config.json").await?;
13431355
13441356 // Pre-load the bundled files before starting the webserver.
13451357 let _ = & * BUNDLED_FILES_MAP ;
1346- let app_data = make_app_data ( addr. port ( ) ) ;
1358+ let app_data = make_app_data ( addr. port ( ) , credentials ) ;
13471359 let app_data_server = app_data. clone ( ) ;
1348- let server =
1349- match HttpServer :: new ( move || configure_app ( App :: new ( ) , & app_data_server) ) . bind ( addr) {
1350- Ok ( server) => server. run ( ) ,
1351- Err ( err) => {
1352- error ! ( "Unable to bind to {addr} - {err}" ) ;
1353- return Err ( err) ;
1354- }
1355- } ;
1360+ let server = match HttpServer :: new ( move || {
1361+ let auth = HttpAuthentication :: with_fn ( basic_validator) ;
1362+ configure_app (
1363+ App :: new ( ) . wrap ( middleware:: Condition :: new (
1364+ app_data_server. credentials . is_some ( ) ,
1365+ auth,
1366+ ) ) ,
1367+ & app_data_server,
1368+ )
1369+ } )
1370+ . bind ( addr)
1371+ {
1372+ Ok ( server) => server. run ( ) ,
1373+ Err ( err) => {
1374+ error ! ( "Unable to bind to {addr} - {err}" ) ;
1375+ return Err ( err) ;
1376+ }
1377+ } ;
13561378 // Store the server handle in the global state.
13571379 * ( app_data. server_handle . lock ( ) . unwrap ( ) ) = Some ( server. handle ( ) ) ;
13581380 // Start the server.
13591381 server. await
13601382}
13611383
1384+ // Use HTTP basic authentication (if provided) to mediate access.
1385+ async fn basic_validator (
1386+ req : ServiceRequest ,
1387+ credentials : BasicAuth ,
1388+ ) -> Result < ServiceRequest , ( Error , ServiceRequest ) > {
1389+ // Get the provided credentials.
1390+ let expected_credentials = & req
1391+ . app_data :: < actix_web:: web:: Data < AppState > > ( )
1392+ . unwrap ( )
1393+ . credentials
1394+ . as_ref ( )
1395+ . unwrap ( ) ;
1396+ if credentials. user_id ( ) == expected_credentials. username
1397+ && credentials. password ( ) == Some ( & expected_credentials. password )
1398+ {
1399+ Ok ( req)
1400+ } else {
1401+ Err ( (
1402+ actix_web:: error:: ErrorUnauthorized ( "Incorrect username or password." ) ,
1403+ req,
1404+ ) )
1405+ }
1406+ }
1407+
13621408pub fn configure_logger ( level : LevelFilter ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
13631409 #[ cfg( not( debug_assertions) ) ]
13641410 let l4rs = ROOT_PATH . clone ( ) ;
@@ -1383,7 +1429,7 @@ pub fn configure_logger(level: LevelFilter) -> Result<(), Box<dyn std::error::Er
13831429// closure passed to `HttpServer::new` and moved/cloned in." Putting this code
13841430// inside `configure_app` places it inside the closure which calls
13851431// `configure_app`, preventing globally shared state.
1386- fn make_app_data ( port : u16 ) -> web:: Data < AppState > {
1432+ fn make_app_data ( port : u16 , credentials : Option < Credentials > ) -> web:: Data < AppState > {
13871433 web:: Data :: new ( AppState {
13881434 server_handle : Mutex :: new ( None ) ,
13891435 connection_id : Mutex :: new ( 0 ) ,
@@ -1393,6 +1439,7 @@ fn make_app_data(port: u16) -> web::Data<AppState> {
13931439 vscode_ide_queues : Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ,
13941440 vscode_client_queues : Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ,
13951441 vscode_connection_id : Arc :: new ( Mutex :: new ( HashSet :: new ( ) ) ) ,
1442+ credentials,
13961443 } )
13971444}
13981445
0 commit comments