@@ -17,11 +17,13 @@ use micro_http::{
1717 Body , HttpHeaderError , MediaType , Method , Request , RequestError , Response , StatusCode , Version ,
1818} ;
1919use serde_json:: { Map , Value } ;
20- use token_headers:: TokenHeaders ;
2120
2221use crate :: mmds:: data_store:: { Mmds , MmdsDatastoreError as MmdsError , MmdsVersion , OutputFormat } ;
2322use crate :: mmds:: token:: PATH_TO_TOKEN ;
24- use crate :: mmds:: token_headers:: REJECTED_HEADER ;
23+ use crate :: mmds:: token_headers:: {
24+ REJECTED_HEADER , X_METADATA_TOKEN_HEADER , X_METADATA_TOKEN_TTL_SECONDS_HEADER ,
25+ get_header_value_pair,
26+ } ;
2527
2628#[ rustfmt:: skip]
2729#[ derive( Debug , thiserror:: Error , displaydoc:: Display ) ]
@@ -142,23 +144,10 @@ fn respond_to_request_mmdsv1(mmds: &Mmds, request: Request) -> Response {
142144}
143145
144146fn respond_to_request_mmdsv2 ( mmds : & mut Mmds , request : Request ) -> Response {
145- // Fetch custom headers from request.
146- let token_headers = match TokenHeaders :: try_from ( request. headers . custom_entries ( ) ) {
147- Ok ( token_headers) => token_headers,
148- Err ( err) => {
149- return build_response (
150- request. http_version ( ) ,
151- StatusCode :: BadRequest ,
152- MediaType :: PlainText ,
153- Body :: new ( err. to_string ( ) ) ,
154- ) ;
155- }
156- } ;
157-
158147 // Allow only GET and PUT requests.
159148 match request. method ( ) {
160- Method :: Get => respond_to_get_request_checked ( mmds, request, token_headers ) ,
161- Method :: Put => respond_to_put_request ( mmds, request, token_headers ) ,
149+ Method :: Get => respond_to_get_request_checked ( mmds, request) ,
150+ Method :: Put => respond_to_put_request ( mmds, request) ,
162151 _ => {
163152 let mut response = build_response (
164153 request. http_version ( ) ,
@@ -173,26 +162,23 @@ fn respond_to_request_mmdsv2(mmds: &mut Mmds, request: Request) -> Response {
173162 }
174163}
175164
176- fn respond_to_get_request_checked (
177- mmds : & Mmds ,
178- request : Request ,
179- token_headers : TokenHeaders ,
180- ) -> Response {
181- // Get MMDS token from custom headers.
182- let token = match token_headers. x_metadata_token ( ) {
183- Some ( token) => token,
184- None => {
185- let error_msg = VmmMmdsError :: NoTokenProvided . to_string ( ) ;
186- return build_response (
187- request. http_version ( ) ,
188- StatusCode :: Unauthorized ,
189- MediaType :: PlainText ,
190- Body :: new ( error_msg) ,
191- ) ;
192- }
193- } ;
165+ fn respond_to_get_request_checked ( mmds : & Mmds , request : Request ) -> Response {
166+ // Check whether a token exists.
167+ let token =
168+ match get_header_value_pair ( request. headers . custom_entries ( ) , X_METADATA_TOKEN_HEADER ) {
169+ Some ( ( _, token) ) => token,
170+ None => {
171+ let error_msg = VmmMmdsError :: NoTokenProvided . to_string ( ) ;
172+ return build_response (
173+ request. http_version ( ) ,
174+ StatusCode :: Unauthorized ,
175+ MediaType :: PlainText ,
176+ Body :: new ( error_msg) ,
177+ ) ;
178+ }
179+ } ;
194180
195- // Validate MMDS token.
181+ // Validate the token.
196182 match mmds. is_valid_token ( token) {
197183 Ok ( true ) => respond_to_get_request_unchecked ( mmds, request) ,
198184 Ok ( false ) => build_response (
@@ -248,17 +234,11 @@ fn respond_to_get_request_unchecked(mmds: &Mmds, request: Request) -> Response {
248234 }
249235}
250236
251- fn respond_to_put_request (
252- mmds : & mut Mmds ,
253- request : Request ,
254- token_headers : TokenHeaders ,
255- ) -> Response {
237+ fn respond_to_put_request ( mmds : & mut Mmds , request : Request ) -> Response {
238+ let custom_headers = request. headers . custom_entries ( ) ;
239+
256240 // Reject `PUT` requests that contain `X-Forwarded-For` header.
257- if request
258- . headers
259- . custom_entries ( )
260- . contains_key ( REJECTED_HEADER )
261- {
241+ if custom_headers. contains_key ( REJECTED_HEADER ) {
262242 let error_msg = RequestError :: HeaderError ( HttpHeaderError :: UnsupportedName (
263243 REJECTED_HEADER . to_string ( ) ,
264244 ) )
@@ -287,17 +267,36 @@ fn respond_to_put_request(
287267 }
288268
289269 // Get token lifetime value.
290- let ttl_seconds = match token_headers. x_metadata_token_ttl_seconds ( ) {
291- Some ( ttl_seconds) => ttl_seconds,
292- None => {
293- return build_response (
294- request. http_version ( ) ,
295- StatusCode :: BadRequest ,
296- MediaType :: PlainText ,
297- Body :: new ( VmmMmdsError :: NoTtlProvided . to_string ( ) ) ,
298- ) ;
299- }
300- } ;
270+ let ttl_seconds =
271+ match get_header_value_pair ( custom_headers, X_METADATA_TOKEN_TTL_SECONDS_HEADER ) {
272+ // Header found
273+ Some ( ( k, v) ) => match v. parse :: < u32 > ( ) {
274+ Ok ( ttl_seconds) => ttl_seconds,
275+ Err ( _) => {
276+ return build_response (
277+ request. http_version ( ) ,
278+ StatusCode :: BadRequest ,
279+ MediaType :: PlainText ,
280+ Body :: new (
281+ RequestError :: HeaderError ( HttpHeaderError :: InvalidValue (
282+ k. into ( ) ,
283+ v. into ( ) ,
284+ ) )
285+ . to_string ( ) ,
286+ ) ,
287+ ) ;
288+ }
289+ } ,
290+ // Header not found
291+ None => {
292+ return build_response (
293+ request. http_version ( ) ,
294+ StatusCode :: BadRequest ,
295+ MediaType :: PlainText ,
296+ Body :: new ( VmmMmdsError :: NoTtlProvided . to_string ( ) ) ,
297+ ) ;
298+ }
299+ } ;
301300
302301 // Generate token.
303302 let result = mmds. generate_token ( ttl_seconds) ;
@@ -530,7 +529,7 @@ mod tests {
530529 let actual_response = convert_to_response ( mmds. clone ( ) , request) ;
531530 assert_eq ! ( actual_response, expected_response) ;
532531
533- // Test invalid custom header value is ignored when V1 is configured .
532+ // Test invalid custom header value is ignored if not PUT request to /latest/api/token .
534533 let ( request, expected_response) = generate_request_and_expected_response (
535534 b"GET http://169.254.169.254/ HTTP/1.0\r \n \
536535 Accept: application/json\r \n \
@@ -576,23 +575,6 @@ mod tests {
576575 let actual_response = convert_to_response ( mmds. clone ( ) , request) ;
577576 assert_eq ! ( actual_response, expected_response) ;
578577
579- // Test invalid value for custom header.
580- let request = Request :: try_from (
581- b"GET http://169.254.169.254/ HTTP/1.0\r \n \
582- Accept: application/json\r \n \
583- X-metadata-token-ttl-seconds: -60\r \n \r \n ",
584- None ,
585- )
586- . unwrap ( ) ;
587- let mut expected_response = Response :: new ( Version :: Http10 , StatusCode :: BadRequest ) ;
588- expected_response. set_content_type ( MediaType :: PlainText ) ;
589- expected_response. set_body ( Body :: new (
590- "Invalid header. Reason: Invalid value. Key:X-metadata-token-ttl-seconds; Value:-60"
591- . to_string ( ) ,
592- ) ) ;
593- let actual_response = convert_to_response ( mmds. clone ( ) , request) ;
594- assert_eq ! ( actual_response, expected_response) ;
595-
596578 // Test PUT requests.
597579 // Unsupported `X-Forwarded-For` header present.
598580 let request = Request :: try_from (
@@ -624,6 +606,22 @@ mod tests {
624606 let actual_response = convert_to_response ( mmds. clone ( ) , request) ;
625607 assert_eq ! ( actual_response, expected_response) ;
626608
609+ // Test invalid X-metadata-token-ttl-seconds value gets BadRequest.
610+ let request = Request :: try_from (
611+ b"PUT http://169.254.169.254/latest/api/token HTTP/1.0\r \n \
612+ X-metadata-token-ttl-seconds: -60\r \n \r \n ",
613+ None ,
614+ )
615+ . unwrap ( ) ;
616+ let mut expected_response = Response :: new ( Version :: Http10 , StatusCode :: BadRequest ) ;
617+ expected_response. set_content_type ( MediaType :: PlainText ) ;
618+ expected_response. set_body ( Body :: new (
619+ "Invalid header. Reason: Invalid value. Key:X-metadata-token-ttl-seconds; Value:-60"
620+ . to_string ( ) ,
621+ ) ) ;
622+ let actual_response = convert_to_response ( mmds. clone ( ) , request) ;
623+ assert_eq ! ( actual_response, expected_response) ;
624+
627625 // Test invalid lifetime values for token.
628626 let invalid_values = [ MIN_TOKEN_TTL_SECONDS - 1 , MAX_TOKEN_TTL_SECONDS + 1 ] ;
629627 for invalid_value in invalid_values. iter ( ) {
@@ -687,6 +685,21 @@ mod tests {
687685 let actual_response = convert_to_response ( mmds. clone ( ) , request) ;
688686 assert_eq ! ( actual_response, expected_response) ;
689687
688+ // Test invalid customer header value is ignored if not PUT request to /latest/api/token.
689+ #[ rustfmt:: skip]
690+ let ( request, expected_response) = generate_request_and_expected_response (
691+ format ! (
692+ "GET http://169.254.169.254/ HTTP/1.0\r \n \
693+ Accept: application/json\r \n \
694+ X-metadata-token: {valid_token}\r \n \
695+ X-metadata-token-ttl-seconds: -60\r \n \r \n ",
696+ )
697+ . as_bytes ( ) ,
698+ MediaType :: ApplicationJson ,
699+ ) ;
700+ let actual_response = convert_to_response ( mmds. clone ( ) , request) ;
701+ assert_eq ! ( actual_response, expected_response) ;
702+
690703 // Test GET request towards unsupported value type.
691704 #[ rustfmt:: skip]
692705 let request = Request :: try_from (
0 commit comments