From d6c399313b826bc39b65217917394d19c01995ce Mon Sep 17 00:00:00 2001 From: Simon Bihel Date: Thu, 22 Jun 2023 10:17:21 +0100 Subject: [PATCH 1/2] Make authorization schemes case and whitespace insensitive According to RFC7235[1]: > It uses a case- > insensitive token as a means to identify the authentication scheme, > followed by additional information necessary for achieving > authentication via that scheme. [1]: https://datatracker.ietf.org/doc/html/rfc7235#section-2.1 --- src/common/authorization.rs | 42 ++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/common/authorization.rs b/src/common/authorization.rs index e62f2fd9..23d56d31 100644 --- a/src/common/authorization.rs +++ b/src/common/authorization.rs @@ -82,9 +82,10 @@ impl ::Header for Authorization { .next() .and_then(|val| { let slice = val.as_bytes(); - if slice.starts_with(C::SCHEME.as_bytes()) - && slice.len() > C::SCHEME.len() + if slice.len() > C::SCHEME.len() && slice[C::SCHEME.len()] == b' ' + && slice[..C::SCHEME.len()].to_ascii_lowercase() + == C::SCHEME.to_ascii_lowercase().as_bytes() { C::decode(val).map(Authorization) } else { @@ -151,7 +152,7 @@ impl Credentials for Basic { fn decode(value: &HeaderValue) -> Option { debug_assert!( - value.as_bytes().starts_with(b"Basic "), + value.as_bytes().to_ascii_lowercase().starts_with(b"basic "), "HeaderValue to decode should start with \"Basic ..\", received = {:?}", value, ); @@ -186,7 +187,7 @@ pub struct Bearer(HeaderValueString); impl Bearer { /// View the token part as a `&str`. pub fn token(&self) -> &str { - &self.0.as_str()["Bearer ".len()..] + self.0.as_str()["Bearer ".len()..].trim_start() } } @@ -195,7 +196,10 @@ impl Credentials for Bearer { fn decode(value: &HeaderValue) -> Option { debug_assert!( - value.as_bytes().starts_with(b"Bearer "), + value + .as_bytes() + .to_ascii_lowercase() + .starts_with(b"bearer "), "HeaderValue to decode should start with \"Bearer ..\", received = {:?}", value, ); @@ -252,6 +256,22 @@ mod tests { assert_eq!(auth.0.password(), "open sesame"); } + #[test] + fn basic_decode_case_insensitive() { + let auth: Authorization = + test_decode(&["basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="]).unwrap(); + assert_eq!(auth.0.username(), "Aladdin"); + assert_eq!(auth.0.password(), "open sesame"); + } + + #[test] + fn basic_decode_extra_whitespaces() { + let auth: Authorization = + test_decode(&["Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="]).unwrap(); + assert_eq!(auth.0.username(), "Aladdin"); + assert_eq!(auth.0.password(), "open sesame"); + } + #[test] fn basic_decode_no_password() { let auth: Authorization = test_decode(&["Basic QWxhZGRpbjo="]).unwrap(); @@ -273,6 +293,18 @@ mod tests { let auth: Authorization = test_decode(&["Bearer fpKL54jvWmEGVoRdCNjG"]).unwrap(); assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG"); } + + #[test] + fn bearer_decode_case_insensitive() { + let auth: Authorization = test_decode(&["bearer fpKL54jvWmEGVoRdCNjG"]).unwrap(); + assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG"); + } + + #[test] + fn bearer_decode_extra_whitespaces() { + let auth: Authorization = test_decode(&["Bearer fpKL54jvWmEGVoRdCNjG"]).unwrap(); + assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG"); + } } //bench_header!(raw, Authorization, { vec![b"foo bar baz".to_vec()] }); From 5a1cc7a13fa4c125b9d32f1866b4b18058ef37ab Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Sat, 18 Nov 2023 11:40:25 -0800 Subject: [PATCH 2/2] Use eq_ignore_ascii_case() for Authorization schemes This is effectively the same as comparing the result of `to_ascii_lowercase()`, with the benefit of avoiding "allocating and copying temporaries" (according to the Rust `std` docs[1]). [1]: https://doc.rust-lang.org/std/primitive.slice.html#method.eq_ignore_ascii_case --- src/common/authorization.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/common/authorization.rs b/src/common/authorization.rs index 23d56d31..bb2c35ec 100644 --- a/src/common/authorization.rs +++ b/src/common/authorization.rs @@ -84,8 +84,7 @@ impl ::Header for Authorization { let slice = val.as_bytes(); if slice.len() > C::SCHEME.len() && slice[C::SCHEME.len()] == b' ' - && slice[..C::SCHEME.len()].to_ascii_lowercase() - == C::SCHEME.to_ascii_lowercase().as_bytes() + && slice[..C::SCHEME.len()].eq_ignore_ascii_case(C::SCHEME.as_bytes()) { C::decode(val).map(Authorization) } else { @@ -152,7 +151,7 @@ impl Credentials for Basic { fn decode(value: &HeaderValue) -> Option { debug_assert!( - value.as_bytes().to_ascii_lowercase().starts_with(b"basic "), + value.as_bytes()[..Self::SCHEME.len()].eq_ignore_ascii_case(Self::SCHEME.as_bytes()), "HeaderValue to decode should start with \"Basic ..\", received = {:?}", value, ); @@ -196,10 +195,7 @@ impl Credentials for Bearer { fn decode(value: &HeaderValue) -> Option { debug_assert!( - value - .as_bytes() - .to_ascii_lowercase() - .starts_with(b"bearer "), + value.as_bytes()[..Self::SCHEME.len()].eq_ignore_ascii_case(Self::SCHEME.as_bytes()), "HeaderValue to decode should start with \"Bearer ..\", received = {:?}", value, );