66
77#![ allow( clippy:: module_name_repetitions) ]
88
9- use std:: sync:: Arc ;
9+ use std:: { net :: IpAddr , ops :: Deref , sync:: Arc } ;
1010
1111use async_graphql:: {
1212 extensions:: Tracing ,
@@ -238,9 +238,10 @@ async fn get_requester(
238238 activity_tracker : & BoundActivityTracker ,
239239 mut repo : BoxRepository ,
240240 session_info : SessionInfo ,
241+ user_agent : Option < String > ,
241242 token : Option < & str > ,
242243) -> Result < Requester , RouteError > {
243- let requester = if let Some ( token) = token {
244+ let entity = if let Some ( token) = token {
244245 // If we haven't enabled undocumented_oauth2_access on the listener, we bail out
245246 if !undocumented_oauth2_access {
246247 return Err ( RouteError :: InvalidToken ) ;
@@ -285,7 +286,7 @@ async fn get_requester(
285286 return Err ( RouteError :: MissingScope ) ;
286287 }
287288
288- Requester :: OAuth2Session ( Box :: new ( ( session, user) ) )
289+ RequestingEntity :: OAuth2Session ( Box :: new ( ( session, user) ) )
289290 } else {
290291 let maybe_session = session_info. load_session ( & mut repo) . await ?;
291292
@@ -295,8 +296,15 @@ async fn get_requester(
295296 . await ;
296297 }
297298
298- Requester :: from ( maybe_session)
299+ RequestingEntity :: from ( maybe_session)
299300 } ;
301+
302+ let requester = Requester {
303+ entity,
304+ ip_address : activity_tracker. ip ( ) ,
305+ user_agent,
306+ } ;
307+
300308 repo. cancel ( ) . await ?;
301309 Ok ( requester)
302310}
@@ -312,20 +320,22 @@ pub async fn post(
312320 cookie_jar : CookieJar ,
313321 content_type : Option < TypedHeader < ContentType > > ,
314322 authorization : Option < TypedHeader < Authorization < Bearer > > > ,
315- requester_fingerprint : RequesterFingerprint ,
323+ user_agent : Option < TypedHeader < headers :: UserAgent > > ,
316324 body : Body ,
317325) -> Result < impl IntoResponse , RouteError > {
318326 let body = body. into_data_stream ( ) ;
319327 let token = authorization
320328 . as_ref ( )
321329 . map ( |TypedHeader ( Authorization ( bearer) ) | bearer. token ( ) ) ;
330+ let user_agent = user_agent. map ( |TypedHeader ( h) | h. to_string ( ) ) ;
322331 let ( session_info, _cookie_jar) = cookie_jar. session_info ( ) ;
323332 let requester = get_requester (
324333 undocumented_oauth2_access,
325334 & clock,
326335 & activity_tracker,
327336 repo,
328337 session_info,
338+ user_agent,
329339 token,
330340 )
331341 . await ?;
@@ -339,7 +349,6 @@ pub async fn post(
339349 MultipartOptions :: default ( ) ,
340350 )
341351 . await ?
342- . data ( requester_fingerprint)
343352 . data ( requester) ; // XXX: this should probably return another error response?
344353
345354 let span = span_for_graphql_request ( & request) ;
@@ -366,26 +375,27 @@ pub async fn get(
366375 activity_tracker : BoundActivityTracker ,
367376 cookie_jar : CookieJar ,
368377 authorization : Option < TypedHeader < Authorization < Bearer > > > ,
369- requester_fingerprint : RequesterFingerprint ,
378+ user_agent : Option < TypedHeader < headers :: UserAgent > > ,
370379 RawQuery ( query) : RawQuery ,
371380) -> Result < impl IntoResponse , FancyError > {
372381 let token = authorization
373382 . as_ref ( )
374383 . map ( |TypedHeader ( Authorization ( bearer) ) | bearer. token ( ) ) ;
384+ let user_agent = user_agent. map ( |TypedHeader ( h) | h. to_string ( ) ) ;
375385 let ( session_info, _cookie_jar) = cookie_jar. session_info ( ) ;
376386 let requester = get_requester (
377387 undocumented_oauth2_access,
378388 & clock,
379389 & activity_tracker,
380390 repo,
381391 session_info,
392+ user_agent,
382393 token,
383394 )
384395 . await ?;
385396
386- let request = async_graphql:: http:: parse_query_string ( & query. unwrap_or_default ( ) ) ?
387- . data ( requester)
388- . data ( requester_fingerprint) ;
397+ let request =
398+ async_graphql:: http:: parse_query_string ( & query. unwrap_or_default ( ) ) ?. data ( requester) ;
389399
390400 let span = span_for_graphql_request ( & request) ;
391401 let response = schema. execute ( request) . instrument ( span) . await ;
@@ -417,9 +427,40 @@ pub fn schema_builder() -> SchemaBuilder {
417427 . register_output_type :: < CreationEvent > ( )
418428}
419429
430+ pub struct Requester {
431+ entity : RequestingEntity ,
432+ ip_address : Option < IpAddr > ,
433+ user_agent : Option < String > ,
434+ }
435+
436+ impl Requester {
437+ pub fn fingerprint ( & self ) -> RequesterFingerprint {
438+ if let Some ( ip) = self . ip_address {
439+ RequesterFingerprint :: new ( ip)
440+ } else {
441+ RequesterFingerprint :: EMPTY
442+ }
443+ }
444+
445+ pub fn for_policy ( & self ) -> mas_policy:: Requester {
446+ mas_policy:: Requester {
447+ ip_address : self . ip_address ,
448+ user_agent : self . user_agent . clone ( ) ,
449+ }
450+ }
451+ }
452+
453+ impl Deref for Requester {
454+ type Target = RequestingEntity ;
455+
456+ fn deref ( & self ) -> & Self :: Target {
457+ & self . entity
458+ }
459+ }
460+
420461/// The identity of the requester.
421462#[ derive( Debug , Clone , Default , PartialEq , Eq ) ]
422- pub enum Requester {
463+ pub enum RequestingEntity {
423464 /// The requester presented no authentication information.
424465 #[ default]
425466 Anonymous ,
@@ -480,7 +521,7 @@ impl OwnerId for UserId {
480521 }
481522}
482523
483- impl Requester {
524+ impl RequestingEntity {
484525 fn browser_session ( & self ) -> Option < & BrowserSession > {
485526 match self {
486527 Self :: BrowserSession ( session) => Some ( session) ,
@@ -532,17 +573,21 @@ impl Requester {
532573 Self :: BrowserSession ( _) | Self :: Anonymous => false ,
533574 }
534575 }
576+
577+ fn is_unauthenticated ( & self ) -> bool {
578+ matches ! ( self , Self :: Anonymous )
579+ }
535580}
536581
537- impl From < BrowserSession > for Requester {
582+ impl From < BrowserSession > for RequestingEntity {
538583 fn from ( session : BrowserSession ) -> Self {
539584 Self :: BrowserSession ( Box :: new ( session) )
540585 }
541586}
542587
543- impl < T > From < Option < T > > for Requester
588+ impl < T > From < Option < T > > for RequestingEntity
544589where
545- T : Into < Requester > ,
590+ T : Into < RequestingEntity > ,
546591{
547592 fn from ( session : Option < T > ) -> Self {
548593 session. map ( Into :: into) . unwrap_or_default ( )
0 commit comments