summaryrefslogtreecommitdiffstats
path: root/third_party/rust/headers/src/common/authorization.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/headers/src/common/authorization.rs')
-rw-r--r--third_party/rust/headers/src/common/authorization.rs276
1 files changed, 276 insertions, 0 deletions
diff --git a/third_party/rust/headers/src/common/authorization.rs b/third_party/rust/headers/src/common/authorization.rs
new file mode 100644
index 0000000000..dbef15301a
--- /dev/null
+++ b/third_party/rust/headers/src/common/authorization.rs
@@ -0,0 +1,276 @@
+//! Authorization header and types.
+
+use base64;
+use bytes::Bytes;
+
+use util::HeaderValueString;
+use HeaderValue;
+
+/// `Authorization` header, defined in [RFC7235](https://tools.ietf.org/html/rfc7235#section-4.2)
+///
+/// The `Authorization` header field allows a user agent to authenticate
+/// itself with an origin server -- usually, but not necessarily, after
+/// receiving a 401 (Unauthorized) response. Its value consists of
+/// credentials containing the authentication information of the user
+/// agent for the realm of the resource being requested.
+///
+/// # ABNF
+///
+/// ```text
+/// Authorization = credentials
+/// ```
+///
+/// # Example values
+/// * `Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==`
+/// * `Bearer fpKL54jvWmEGVoRdCNjG`
+///
+/// # Examples
+///
+/// ```
+/// # extern crate headers;
+/// use headers::Authorization;
+///
+/// let basic = Authorization::basic("Aladdin", "open sesame");
+/// let bearer = Authorization::bearer("some-opaque-token").unwrap();
+/// ```
+///
+#[derive(Clone, PartialEq, Debug)]
+pub struct Authorization<C: Credentials>(pub C);
+
+impl Authorization<Basic> {
+ /// Create a `Basic` authorization header.
+ pub fn basic(username: &str, password: &str) -> Self {
+ let colon_pos = username.len();
+ let decoded = format!("{}:{}", username, password);
+
+ Authorization(Basic { decoded, colon_pos })
+ }
+
+ /// View the decoded username.
+ pub fn username(&self) -> &str {
+ self.0.username()
+ }
+
+ /// View the decoded password.
+ pub fn password(&self) -> &str {
+ self.0.password()
+ }
+}
+
+impl Authorization<Bearer> {
+ /// Try to create a `Bearer` authorization header.
+ pub fn bearer(token: &str) -> Result<Self, InvalidBearerToken> {
+ HeaderValueString::from_string(format!("Bearer {}", token))
+ .map(|val| Authorization(Bearer(val)))
+ .ok_or_else(|| InvalidBearerToken { _inner: () })
+ }
+
+ /// View the token part as a `&str`.
+ pub fn token(&self) -> &str {
+ self.0.token()
+ }
+}
+
+impl<C: Credentials> ::Header for Authorization<C> {
+ fn name() -> &'static ::HeaderName {
+ &::http::header::AUTHORIZATION
+ }
+
+ fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, ::Error> {
+ values
+ .next()
+ .and_then(|val| {
+ let slice = val.as_bytes();
+ if slice.starts_with(C::SCHEME.as_bytes())
+ && slice.len() > C::SCHEME.len()
+ && slice[C::SCHEME.len()] == b' '
+ {
+ C::decode(val).map(Authorization)
+ } else {
+ None
+ }
+ })
+ .ok_or_else(::Error::invalid)
+ }
+
+ fn encode<E: Extend<::HeaderValue>>(&self, values: &mut E) {
+ let value = self.0.encode();
+ debug_assert!(
+ value.as_bytes().starts_with(C::SCHEME.as_bytes()),
+ "Credentials::encode should include its scheme: scheme = {:?}, encoded = {:?}",
+ C::SCHEME,
+ value,
+ );
+
+ values.extend(::std::iter::once(value));
+ }
+}
+
+/// Credentials to be used in the `Authorization` header.
+pub trait Credentials: Sized {
+ /// The scheme identify the format of these credentials.
+ ///
+ /// This is the static string that always prefixes the actual credentials,
+ /// like `"Basic"` in basic authorization.
+ const SCHEME: &'static str;
+
+ /// Try to decode the credentials from the `HeaderValue`.
+ ///
+ /// The `SCHEME` will be the first part of the `value`.
+ fn decode(value: &HeaderValue) -> Option<Self>;
+
+ /// Encode the credentials to a `HeaderValue`.
+ ///
+ /// The `SCHEME` must be the first part of the `value`.
+ fn encode(&self) -> HeaderValue;
+}
+
+/// Credential holder for Basic Authentication
+#[derive(Clone, PartialEq, Debug)]
+pub struct Basic {
+ decoded: String,
+ colon_pos: usize,
+}
+
+impl Basic {
+ /// View the decoded username.
+ pub fn username(&self) -> &str {
+ &self.decoded[..self.colon_pos]
+ }
+
+ /// View the decoded password.
+ pub fn password(&self) -> &str {
+ &self.decoded[self.colon_pos + 1..]
+ }
+}
+
+impl Credentials for Basic {
+ const SCHEME: &'static str = "Basic";
+
+ fn decode(value: &HeaderValue) -> Option<Self> {
+ debug_assert!(
+ value.as_bytes().starts_with(b"Basic "),
+ "HeaderValue to decode should start with \"Basic ..\", received = {:?}",
+ value,
+ );
+
+ let bytes = &value.as_bytes()["Basic ".len()..];
+ let non_space_pos = bytes.iter().position(|b| *b != b' ')?;
+ let bytes = &bytes[non_space_pos..];
+ let bytes = base64::decode(bytes).ok()?;
+
+ let decoded = String::from_utf8(bytes).ok()?;
+
+ let colon_pos = decoded.find(':')?;
+
+ Some(Basic { decoded, colon_pos })
+ }
+
+ fn encode(&self) -> HeaderValue {
+ let mut encoded = String::from("Basic ");
+ base64::encode_config_buf(&self.decoded, base64::STANDARD, &mut encoded);
+
+ let bytes = Bytes::from(encoded);
+ HeaderValue::from_maybe_shared(bytes).expect("base64 encoding is always a valid HeaderValue")
+ }
+}
+
+#[derive(Clone, PartialEq, Debug)]
+/// Token holder for Bearer Authentication, most often seen with oauth
+pub struct Bearer(HeaderValueString);
+
+impl Bearer {
+ /// View the token part as a `&str`.
+ pub fn token(&self) -> &str {
+ &self.0.as_str()["Bearer ".len()..]
+ }
+}
+
+impl Credentials for Bearer {
+ const SCHEME: &'static str = "Bearer";
+
+ fn decode(value: &HeaderValue) -> Option<Self> {
+ debug_assert!(
+ value.as_bytes().starts_with(b"Bearer "),
+ "HeaderValue to decode should start with \"Bearer ..\", received = {:?}",
+ value,
+ );
+
+ HeaderValueString::from_val(value).ok().map(Bearer)
+ }
+
+ fn encode(&self) -> HeaderValue {
+ (&self.0).into()
+ }
+}
+
+error_type!(InvalidBearerToken);
+
+#[cfg(test)]
+mod tests {
+ use super::super::{test_decode, test_encode};
+ use super::{Authorization, Basic, Bearer};
+ use http::header::HeaderMap;
+ use HeaderMapExt;
+
+ #[test]
+ fn basic_encode() {
+ let auth = Authorization::basic("Aladdin", "open sesame");
+ let headers = test_encode(auth);
+
+ assert_eq!(
+ headers["authorization"],
+ "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
+ );
+ }
+
+ #[test]
+ fn basic_roundtrip() {
+ let auth = Authorization::basic("Aladdin", "open sesame");
+ let mut h = HeaderMap::new();
+ h.typed_insert(auth.clone());
+ assert_eq!(h.typed_get(), Some(auth));
+ }
+
+ #[test]
+ fn basic_encode_no_password() {
+ let auth = Authorization::basic("Aladdin", "");
+ let headers = test_encode(auth);
+
+ assert_eq!(headers["authorization"], "Basic QWxhZGRpbjo=",);
+ }
+
+ #[test]
+ fn basic_decode() {
+ let auth: Authorization<Basic> =
+ 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<Basic> = test_decode(&["Basic QWxhZGRpbjo="]).unwrap();
+ assert_eq!(auth.0.username(), "Aladdin");
+ assert_eq!(auth.0.password(), "");
+ }
+
+ #[test]
+ fn bearer_encode() {
+ let auth = Authorization::bearer("fpKL54jvWmEGVoRdCNjG").unwrap();
+
+ let headers = test_encode(auth);
+
+ assert_eq!(headers["authorization"], "Bearer fpKL54jvWmEGVoRdCNjG",);
+ }
+
+ #[test]
+ fn bearer_decode() {
+ let auth: Authorization<Bearer> = test_decode(&["Bearer fpKL54jvWmEGVoRdCNjG"]).unwrap();
+ assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG");
+ }
+}
+
+//bench_header!(raw, Authorization<String>, { vec![b"foo bar baz".to_vec()] });
+//bench_header!(basic, Authorization<Basic>, { vec![b"Basic QWxhZGRpbjpuIHNlc2FtZQ==".to_vec()] });
+//bench_header!(bearer, Authorization<Bearer>, { vec![b"Bearer fpKL54jvWmEGVoRdCNjG".to_vec()] });