@@ -10,10 +10,15 @@ use alvr_events::{ButtonEvent, EventType};
1010use alvr_packets:: { ButtonEntry , ClientListAction , ServerRequest } ;
1111use bytes:: Buf ;
1212use futures:: SinkExt ;
13- use headers:: HeaderMapExt ;
13+ use headers:: {
14+ AccessControlAllowHeaders , AccessControlAllowMethods , AccessControlRequestHeaders ,
15+ AccessControlRequestMethod , HeaderMapExt ,
16+ } ;
1417use hyper:: {
15- header:: { self , HeaderValue , ACCESS_CONTROL_ALLOW_ORIGIN , CACHE_CONTROL } ,
16- service, Body , Request , Response , StatusCode ,
18+ header:: {
19+ self , HeaderName , HeaderValue , ACCESS_CONTROL_ALLOW_ORIGIN , CACHE_CONTROL , CONTENT_TYPE ,
20+ } ,
21+ service, Body , Method , Request , Response , StatusCode ,
1722} ;
1823use serde:: de:: DeserializeOwned ;
1924use serde_json as json;
@@ -88,6 +93,93 @@ async fn http_api(
8893 connection_context : & ConnectionContext ,
8994 request : Request < Body > ,
9095) -> Result < Response < Body > > {
96+ let allow_untrusted_http = SESSION_MANAGER
97+ . read ( )
98+ . session ( )
99+ . session_settings
100+ . connection
101+ . allow_untrusted_http ;
102+
103+ const X_ALVR : & str = "X-ALVR" ;
104+
105+ // A browser is asking for CORS info
106+ if request. method ( ) == Method :: OPTIONS {
107+ let bad_request: Response < Body > = Response :: builder ( )
108+ . status ( StatusCode :: FORBIDDEN )
109+ . body ( "" . into ( ) ) ?;
110+
111+ if !allow_untrusted_http {
112+ return Ok ( bad_request) ;
113+ }
114+
115+ if let Some ( requested_method) = request. headers ( ) . typed_get :: < AccessControlRequestMethod > ( )
116+ {
117+ if requested_method != Method :: GET . into ( ) && requested_method != Method :: POST . into ( ) {
118+ return Ok ( bad_request) ;
119+ }
120+ } else {
121+ return Ok ( bad_request) ;
122+ }
123+
124+ if let Some ( requested_headers) =
125+ request. headers ( ) . typed_get :: < AccessControlRequestHeaders > ( )
126+ {
127+ let mut found_x_alvr = false ;
128+ for header in requested_headers. iter ( ) {
129+ if header == HeaderName :: from_static ( X_ALVR ) {
130+ found_x_alvr = true ;
131+ } else if header != CONTENT_TYPE {
132+ return Ok ( bad_request) ;
133+ }
134+ }
135+
136+ // Ensure it actually requested the X-ALVR header, because we don't want to allow it
137+ // if it never got asked for
138+ if !found_x_alvr {
139+ return Ok ( bad_request) ;
140+ }
141+ } else {
142+ return Ok ( bad_request) ;
143+ }
144+
145+ let allowed_methods = [ Method :: GET , Method :: POST , Method :: OPTIONS ]
146+ . into_iter ( )
147+ . collect :: < AccessControlAllowMethods > ( ) ;
148+ let allowed_headers = [ CONTENT_TYPE , HeaderName :: from_static ( X_ALVR ) ]
149+ . into_iter ( )
150+ . collect :: < AccessControlAllowHeaders > ( ) ;
151+
152+ let mut response: Response < Body > = Response :: builder ( )
153+ . status ( StatusCode :: OK )
154+ . header ( CACHE_CONTROL , "no-cache, no-store, must-revalidate" )
155+ . header ( ACCESS_CONTROL_ALLOW_ORIGIN , "*" )
156+ . body ( "" . into ( ) ) ?;
157+
158+ let headers = response. headers_mut ( ) ;
159+ headers. typed_insert ( allowed_methods) ;
160+ headers. typed_insert ( allowed_headers) ;
161+
162+ return Ok ( response) ;
163+ }
164+
165+ if request. method ( ) != Method :: POST && request. method ( ) != Method :: GET {
166+ return Ok ( Response :: builder ( )
167+ . status ( StatusCode :: BAD_REQUEST )
168+ . body ( "invalid method" . into ( ) ) ?) ;
169+ }
170+
171+ // This is the actual core part of cors
172+ // We require the X-ALVR header, but the browser forces a cors preflight
173+ // if the site tries to send a request with it set since it's not-whitelisted
174+ //
175+ // The dashboard can just set the header and be allowed through without the preflight
176+ // thus not getting blocked by allow_untrusted_http being disabled
177+ if request. headers ( ) . get ( X_ALVR ) != Some ( & HeaderValue :: from_static ( "true" ) ) {
178+ return Ok ( Response :: builder ( )
179+ . status ( StatusCode :: BAD_REQUEST )
180+ . body ( "missing X-ALVR header" . into ( ) ) ?) ;
181+ }
182+
91183 let mut response = match request. uri ( ) . path ( ) {
92184 // New unified requests
93185 "/api/dashboard-request" => {
0 commit comments