@@ -11,7 +11,7 @@ use crate::{
11
11
sasl:: { SaslResponse , SaslStart } ,
12
12
AuthMechanism ,
13
13
} ,
14
- options:: ServerApi ,
14
+ options:: { ServerAddress , ServerApi } ,
15
15
} ,
16
16
cmap:: { Command , Connection } ,
17
17
error:: { Error , Result } ,
@@ -25,6 +25,15 @@ const HUMAN_CALLBACK_TIMEOUT: Duration = Duration::from_secs(5 * 60);
25
25
const MACHINE_CALLBACK_TIMEOUT : Duration = Duration :: from_secs ( 60 ) ;
26
26
const MACHINE_INVALIDATE_SLEEP_TIMEOUT : Duration = Duration :: from_millis ( 100 ) ;
27
27
const API_VERSION : u32 = 1 ;
28
+ const DEFAULT_ALLOWED_HOSTS : & [ & str ] = & [
29
+ "*.mongodb.net" ,
30
+ "*.mongodb-qa.net" ,
31
+ "*.mongodb-dev.net" ,
32
+ "*.mongodbgov.net" ,
33
+ "localhost" ,
34
+ "127.0.0.1" ,
35
+ "::1" ,
36
+ ] ;
28
37
29
38
/// The user-supplied callbacks for OIDC authentication.
30
39
#[ derive( Clone ) ]
@@ -351,12 +360,54 @@ async fn do_two_step_auth(
351
360
Ok ( ( ) )
352
361
}
353
362
363
+ fn get_allowed_hosts ( mechanism_properties : Option < & Document > ) -> Result < Vec < & str > > {
364
+ if mechanism_properties. is_none ( ) {
365
+ return Ok ( Vec :: from ( DEFAULT_ALLOWED_HOSTS ) ) ;
366
+ }
367
+ if let Some ( allowed_hosts) =
368
+ mechanism_properties. and_then ( |p| p. get_array ( "ALLOWED_HOSTS" ) . ok ( ) )
369
+ {
370
+ return allowed_hosts
371
+ . iter ( )
372
+ . map ( |host| {
373
+ host. as_str ( )
374
+ . ok_or_else ( || auth_error ( "ALLOWED_HOSTS must contain only strings" ) )
375
+ } )
376
+ . collect :: < Result < Vec < _ > > > ( ) ;
377
+ }
378
+ Ok ( Vec :: from ( DEFAULT_ALLOWED_HOSTS ) )
379
+ }
380
+
381
+ fn validate_address_with_allowed_hosts (
382
+ mechanism_properties : Option < & Document > ,
383
+ address : & ServerAddress ,
384
+ ) -> Result < ( ) > {
385
+ let hostname = if let ServerAddress :: Tcp { host, .. } = address {
386
+ host. as_str ( )
387
+ } else {
388
+ return Err ( auth_error ( "OIDC human flow only supports TCP addresses" ) ) ;
389
+ } ;
390
+ for pattern in get_allowed_hosts ( mechanism_properties) ? {
391
+ if pattern == hostname {
392
+ return Ok ( ( ) ) ;
393
+ }
394
+ if pattern. starts_with ( "*." ) && hostname. ends_with ( & pattern[ 1 ..] ) {
395
+ return Ok ( ( ) ) ;
396
+ }
397
+ }
398
+ Err ( auth_error (
399
+ "The Connection address is not in the allowed list of hosts" ,
400
+ ) )
401
+ }
402
+
354
403
async fn authenticate_human (
355
404
conn : & mut Connection ,
356
405
credential : & Credential ,
357
406
server_api : Option < & ServerApi > ,
358
407
callback : Arc < CallbackInner > ,
359
408
) -> Result < ( ) > {
409
+ validate_address_with_allowed_hosts ( credential. mechanism_properties . as_ref ( ) , & conn. address ) ?;
410
+
360
411
let source = credential. source . as_deref ( ) . unwrap_or ( "$external" ) ;
361
412
362
413
// If the access token is in the cache, we can use it to send the sasl start command and avoid
0 commit comments