restructure to modules

pull/4/head
Ondřej Hruška 5 years ago
parent c81e3caf17
commit f4c6e4ab3d
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 1
      Cargo.toml
  2. 311
      src/digest.rs
  3. 182
      src/enums.rs
  4. 31
      src/error.rs
  5. 16
      src/lib.rs
  6. 18
      src/utils.rs

@ -18,4 +18,3 @@ license = "MIT"
rust-crypto = "0.2" rust-crypto = "0.2"
rand = "0.6" rand = "0.6"
hex = "0.3.2" hex = "0.3.2"
failure = "0.1.5"

@ -1,219 +1,29 @@
use crate::utils::QuoteForDigest; use rand::Rng;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
use failure::{Error,Fallible};
use crypto::{
digest::Digest,
md5::Md5,
sha2::Sha256,
sha2::Sha512Trunc256
};
use rand::Rng;
//region Algorithm
/// Algorithm type
#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types)]
pub enum AlgorithmType {
MD5,
SHA2_256,
SHA2_512_256,
}
/// Algorithm and the -sess flag pair
#[derive(Debug, PartialEq)]
pub struct Algorithm {
pub algo: AlgorithmType,
pub sess: bool,
}
impl Algorithm {
/// Compose from algorithm type and the -sess flag
pub fn new(algo: AlgorithmType, sess: bool) -> Algorithm {
Algorithm { algo, sess }
}
/// Calculate a hash of bytes using the selected algorithm
pub fn hash(&self, bytes: &[u8]) -> String {
let mut hash: Box<dyn Digest> = match self.algo {
AlgorithmType::MD5 => Box::new(Md5::new()),
AlgorithmType::SHA2_256 => Box::new(Sha256::new()),
AlgorithmType::SHA2_512_256 => Box::new(Sha512Trunc256::new()),
};
hash.input(bytes);
hash.result_str()
}
/// Calculate a hash of string's bytes using the selected algorithm
pub fn hash_str(&self, bytes: &str) -> String {
self.hash(bytes.as_bytes())
}
}
impl FromStr for Algorithm {
type Err = Error;
/// Parse from the format used in WWW-Authorization
fn from_str(s: &str) -> Fallible<Self> {
match s {
"MD5" => Ok(Algorithm::new(AlgorithmType::MD5, false)),
"MD5-sess" => Ok(Algorithm::new(AlgorithmType::MD5, true)),
"SHA-256" => Ok(Algorithm::new(AlgorithmType::SHA2_256, false)),
"SHA-256-sess" => Ok(Algorithm::new(AlgorithmType::SHA2_256, true)),
"SHA-512-256" => Ok(Algorithm::new(AlgorithmType::SHA2_512_256, false)),
"SHA-512-256-sess" => Ok(Algorithm::new(AlgorithmType::SHA2_512_256, true)),
_ => Err(format_err!("Unknown algorithm: {}", s)),
}
}
}
impl Default for Algorithm {
/// Get a MD5 instance
fn default() -> Self {
Algorithm::new(AlgorithmType::MD5, false)
}
}
impl Display for Algorithm {
/// Format to the form used in HTTP headers
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
f.write_str(match self.algo {
AlgorithmType::MD5 => "MD5",
AlgorithmType::SHA2_256 => "SHA-256",
AlgorithmType::SHA2_512_256 => "SHA-512-256",
})?;
if self.sess {
f.write_str("-sess")?;
}
Ok(())
}
}
//endregion
//region Qop
/// QOP field values
#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types)]
pub enum Qop {
/// QOP field not set by server
AUTH,
AUTH_INT,
}
impl FromStr for Qop {
type Err = Error;
/// Parse from "auth" or "auth-int" as used in HTTP headers
fn from_str(s: &str) -> Fallible<Self> {
match s {
"auth" => Ok(Qop::AUTH),
"auth-int" => Ok(Qop::AUTH_INT),
_ => Err(format_err!("Unknown QOP value: {}", s)),
}
}
}
impl Display for Qop {
/// Convert to "auth" or "auth-int" as used in HTTP headers
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
f.write_str(match self {
Qop::AUTH => "auth",
Qop::AUTH_INT => "auth-int",
})?;
Ok(())
}
}
#[derive(Debug)]
#[allow(non_camel_case_types)]
enum QopAlgo<'a> {
NONE,
AUTH,
AUTH_INT(&'a [u8]),
}
// casting back...
impl<'a> Into<Option<Qop>> for QopAlgo<'a> {
/// Convert to ?Qop
fn into(self) -> Option<Qop> {
match self {
QopAlgo::NONE => None,
QopAlgo::AUTH => Some(Qop::AUTH),
QopAlgo::AUTH_INT(_) => Some(Qop::AUTH_INT),
}
}
}
//endregion
//region Charset
/// Charset field value as specified by the server
#[derive(Debug, PartialEq)]
pub enum Charset {
ASCII,
UTF8,
}
impl FromStr for Charset {
type Err = Error;
/// Parse from string (only UTF-8 supported, as prescribed by the specification)
fn from_str(s: &str) -> Fallible<Self> {
match s {
"UTF-8" => Ok(Charset::UTF8),
_ => Err(format_err!("Unknown charset value: {}", s)),
}
}
}
//endregion use crate::enums::{Algorithm, AlgorithmType, Charset, HttpMethod, Qop, QopAlgo};
//region HttpMethod use crate::{Error::*, Result};
/// HTTP method (used when generating the response hash for some Qop options) /// slash quoting for digest strings
#[derive(Debug)] trait QuoteForDigest {
pub enum HttpMethod { fn quote_for_digest(&self) -> String;
GET,
POST,
HEAD,
OTHER(&'static str),
} }
impl Default for HttpMethod { impl QuoteForDigest for &str {
fn default() -> Self { fn quote_for_digest(&self) -> String {
HttpMethod::GET self.to_string().quote_for_digest()
} }
} }
impl Display for HttpMethod { impl QuoteForDigest for String {
/// Convert to uppercase string fn quote_for_digest(&self) -> String {
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { self.replace("\\", "\\\\").replace("\"", "\\\"")
f.write_str(match self {
HttpMethod::GET => "GET",
HttpMethod::POST => "POST",
HttpMethod::HEAD => "HEAD",
HttpMethod::OTHER(s) => s,
})?;
Ok(())
} }
} }
//endregion
//region AuthContext
/// Login attempt context /// Login attempt context
/// ///
/// All fields are borrowed to reduce runtime overhead; this struct should not be stored anywhere, /// All fields are borrowed to reduce runtime overhead; this struct should not be stored anywhere,
@ -248,7 +58,7 @@ impl<'a> AuthContext<'a> {
username: &'n str, username: &'n str,
password: &'p str, password: &'p str,
uri: &'u str, uri: &'u str,
body : Option<&'b [u8]> body: Option<&'b [u8]>,
) -> Self { ) -> Self {
Self::new_with_method(username, password, uri, body, HttpMethod::POST) Self::new_with_method(username, password, uri, body, HttpMethod::POST)
} }
@ -259,7 +69,7 @@ impl<'a> AuthContext<'a> {
password: &'p str, password: &'p str,
uri: &'u str, uri: &'u str,
body: Option<&'b [u8]>, body: Option<&'b [u8]>,
method : HttpMethod method: HttpMethod,
) -> Self { ) -> Self {
Self { Self {
username, username,
@ -267,19 +77,16 @@ impl<'a> AuthContext<'a> {
uri, uri,
body, body,
method, method,
cnonce: None cnonce: None,
} }
} }
/// Set cnonce to the given value
pub fn set_custom_cnonce<'x: 'a>(&mut self, cnonce: &'x str) { pub fn set_custom_cnonce<'x: 'a>(&mut self, cnonce: &'x str) {
self.cnonce = Some(cnonce); self.cnonce = Some(cnonce);
} }
} }
//endregion
//region WwwAuthenticateHeader
/// WWW-Authenticate header parsed from HTTP header value /// WWW-Authenticate header parsed from HTTP header value
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct WwwAuthenticateHeader { pub struct WwwAuthenticateHeader {
@ -308,10 +115,22 @@ pub struct WwwAuthenticateHeader {
pub nc: u32, pub nc: u32,
} }
impl FromStr for WwwAuthenticateHeader {
type Err = crate::Error;
/// Parse HTTP header
fn from_str(input: &str) -> Result<Self> {
Self::parse(input)
}
}
impl WwwAuthenticateHeader { impl WwwAuthenticateHeader {
/// Generate an [`AuthorizationHeader`](struct.AuthorizationHeader.html) to be sent to the server in a new request. /// Generate an [`AuthorizationHeader`](struct.AuthorizationHeader.html) to be sent to the server in a new request.
/// The [`self.nc`](struct.AuthorizationHeader.html#structfield.nc) field is incremented. /// The [`self.nc`](struct.AuthorizationHeader.html#structfield.nc) field is incremented.
pub fn respond<'re, 'a:'re, 'c:'re>(&'a mut self, secrets : &'c AuthContext) -> Fallible<AuthorizationHeader<'re>> { pub fn respond<'re, 'a: 're, 'c: 're>(
&'a mut self,
secrets: &'c AuthContext,
) -> Result<AuthorizationHeader<'re>> {
AuthorizationHeader::from_prompt(self, secrets) AuthorizationHeader::from_prompt(self, secrets)
} }
@ -319,8 +138,10 @@ impl WwwAuthenticateHeader {
/// ///
/// # Errors /// # Errors
/// If the header is malformed (e.g. missing 'realm', missing a closing quote, unknown algorithm etc.) /// If the header is malformed (e.g. missing 'realm', missing a closing quote, unknown algorithm etc.)
pub fn parse(input: &str) -> Fallible<Self> { pub fn parse(input: &str) -> Result<Self> {
let mut input = input.trim(); let mut input = input.trim();
// Remove leading "Digest"
if input.starts_with("Digest") { if input.starts_with("Digest") {
input = &input["Digest".len()..]; input = &input["Digest".len()..];
} }
@ -343,11 +164,11 @@ impl WwwAuthenticateHeader {
}, },
realm: match kv.remove("realm") { realm: match kv.remove("realm") {
Some(v) => v, Some(v) => v,
None => bail!("realm not given"), None => return Err(MissingRealm(input.into())),
}, },
nonce: match kv.remove("nonce") { nonce: match kv.remove("nonce") {
Some(v) => v, Some(v) => v,
None => bail!("nonce not given"), None => return Err(MissingNonce(input.into())),
}, },
opaque: kv.remove("opaque"), opaque: kv.remove("opaque"),
stale: match kv.get("stale") { stale: match kv.get("stale") {
@ -373,13 +194,13 @@ impl WwwAuthenticateHeader {
Some(v) => &v.to_ascii_lowercase() == "true", Some(v) => &v.to_ascii_lowercase() == "true",
None => false, None => false,
}, },
nc : 0 nc: 0,
}) })
} }
} }
/// Helper func that parses the key-value string received from server /// Helper func that parses the key-value string received from server
pub fn parse_header_map(input: &str) -> Fallible<HashMap<String, String>> { fn parse_header_map(input: &str) -> Result<HashMap<String, String>> {
#[derive(Debug)] #[derive(Debug)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
enum ParserState { enum ParserState {
@ -462,28 +283,17 @@ pub fn parse_header_map(input: &str) -> Fallible<HashMap<String, String>> {
parsed.insert(current_token.unwrap().to_string(), current_value); // consume the value here parsed.insert(current_token.unwrap().to_string(), current_value); // consume the value here
} }
ParserState::P_WHITE => {} ParserState::P_WHITE => {}
_ => bail!("Unexpected end state {:?}", state), _ => return Err(InvalidHeaderSyntax(input.into())),
} }
Ok(parsed) Ok(parsed)
} }
impl FromStr for WwwAuthenticateHeader {
type Err = Error;
/// Parse HTTP header
fn from_str(input: &str) -> Fallible<Self> {
Self::parse(input)
}
}
//endregion
//region AuthorizationHeader
/// Header sent back to the server, including password hashes. /// Header sent back to the server, including password hashes.
/// ///
/// This can be obtained by calling [`AuthorizationHeader::from_prompt()`](#method.from_prompt), or from the [`WwwAuthenticateHeader`](struct.WwwAuthenticateHeader.html) prompt struct with [`.respond()`](struct.WwwAuthenticateHeader.html#method.respond) /// This can be obtained by calling [`AuthorizationHeader::from_prompt()`](#method.from_prompt),
/// or from the [`WwwAuthenticateHeader`](struct.WwwAuthenticateHeader.html) prompt struct
/// with [`.respond()`](struct.WwwAuthenticateHeader.html#method.respond)
#[derive(Debug)] #[derive(Debug)]
pub struct AuthorizationHeader<'ctx> { pub struct AuthorizationHeader<'ctx> {
/// The server header that triggered the authentication flow; used to retrieve some additional /// The server header that triggered the authentication flow; used to retrieve some additional
@ -520,8 +330,9 @@ impl<'a> AuthorizationHeader<'a> {
/// Fails if the source header is malformed so much that we can't figure out /// Fails if the source header is malformed so much that we can't figure out
/// a proper response (e.g. given but invalid QOP options) /// a proper response (e.g. given but invalid QOP options)
pub fn from_prompt<'p: 'a, 's: 'a>( pub fn from_prompt<'p: 'a, 's: 'a>(
prompt: &'p mut WwwAuthenticateHeader, context: &'s AuthContext prompt: &'p mut WwwAuthenticateHeader,
) -> Fallible<AuthorizationHeader<'a>> { context: &'s AuthContext,
) -> Result<AuthorizationHeader<'a>> {
// figure out which QOP option to use // figure out which QOP option to use
let empty_vec = vec![]; let empty_vec = vec![];
let qop_algo = match &prompt.qop { let qop_algo = match &prompt.qop {
@ -544,7 +355,12 @@ impl<'a> AuthorizationHeader<'a> {
QopAlgo::AUTH QopAlgo::AUTH
} else { } else {
// parser bug - prompt.qop should have been None // parser bug - prompt.qop should have been None
bail!("Bad QOP options - {:#?}", vec); return Err(BadQopOptions(
vec.iter()
.map(ToString::to_string)
.collect::<Vec<String>>()
.join(","),
));
} }
} }
}; };
@ -604,7 +420,8 @@ impl<'a> AuthorizationHeader<'a> {
"{username}:{realm}", "{username}:{realm}",
username = context.username, username = context.username,
realm = prompt.realm realm = prompt.realm
).as_bytes() )
.as_bytes(),
) )
} else { } else {
context.username.to_owned() context.username.to_owned()
@ -662,7 +479,7 @@ impl<'a> AuthorizationHeader<'a> {
} }
impl<'a> Display for AuthorizationHeader<'a> { impl<'a> Display for AuthorizationHeader<'a> {
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("Digest ")?; f.write_str("Digest ")?;
//TODO charset shenanigans with username* (UTF-8 charset) //TODO charset shenanigans with username* (UTF-8 charset)
@ -714,21 +531,17 @@ impl<'a> Display for AuthorizationHeader<'a> {
} }
} }
//endregion
//region TESTS
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr; use super::parse_header_map;
use super::WwwAuthenticateHeader;
use super::AuthorizationHeader;
use super::Algorithm; use super::Algorithm;
use super::AlgorithmType;
use super::AuthorizationHeader;
use super::Charset; use super::Charset;
use super::Qop; use super::Qop;
use super::AlgorithmType; use super::WwwAuthenticateHeader;
use super::parse_header_map;
use crate::digest::AuthContext; use crate::digest::AuthContext;
use std::str::FromStr;
#[test] #[test]
fn test_parse_header_map() { fn test_parse_header_map() {
@ -812,7 +625,7 @@ mod tests {
qop: Some(vec![Qop::AUTH]), qop: Some(vec![Qop::AUTH]),
userhash: true, userhash: true,
charset: Charset::UTF8, charset: Charset::UTF8,
nc: 0 nc: 0,
} }
) )
} }
@ -840,7 +653,7 @@ mod tests {
qop: Some(vec![Qop::AUTH_INT]), qop: Some(vec![Qop::AUTH_INT]),
userhash: false, userhash: false,
charset: Charset::ASCII, charset: Charset::ASCII,
nc: 0 nc: 0,
} }
) )
} }
@ -863,7 +676,7 @@ mod tests {
qop: None, qop: None,
userhash: false, userhash: false,
charset: Charset::ASCII, charset: Charset::ASCII,
nc: 0 nc: 0,
} }
) )
} }
@ -1021,5 +834,3 @@ Digest username="Mufasa",
); );
} }
} }
//endregion

@ -0,0 +1,182 @@
use crate::{Error, Error::*, Result};
use crypto::{digest::Digest, md5::Md5, sha2::Sha256, sha2::Sha512Trunc256};
use std::fmt;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
/// Algorithm type
#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types)]
pub enum AlgorithmType {
MD5,
SHA2_256,
SHA2_512_256,
}
/// Algorithm and the -sess flag pair
#[derive(Debug, PartialEq)]
pub struct Algorithm {
pub algo: AlgorithmType,
pub sess: bool,
}
impl Algorithm {
/// Compose from algorithm type and the -sess flag
pub fn new(algo: AlgorithmType, sess: bool) -> Algorithm {
Algorithm { algo, sess }
}
/// Calculate a hash of bytes using the selected algorithm
pub fn hash(&self, bytes: &[u8]) -> String {
let mut hash: Box<dyn Digest> = match self.algo {
AlgorithmType::MD5 => Box::new(Md5::new()),
AlgorithmType::SHA2_256 => Box::new(Sha256::new()),
AlgorithmType::SHA2_512_256 => Box::new(Sha512Trunc256::new()),
};
hash.input(bytes);
hash.result_str()
}
/// Calculate a hash of string's bytes using the selected algorithm
pub fn hash_str(&self, bytes: &str) -> String {
self.hash(bytes.as_bytes())
}
}
impl FromStr for Algorithm {
type Err = Error;
/// Parse from the format used in WWW-Authorization
fn from_str(s: &str) -> Result<Self> {
match s {
"MD5" => Ok(Algorithm::new(AlgorithmType::MD5, false)),
"MD5-sess" => Ok(Algorithm::new(AlgorithmType::MD5, true)),
"SHA-256" => Ok(Algorithm::new(AlgorithmType::SHA2_256, false)),
"SHA-256-sess" => Ok(Algorithm::new(AlgorithmType::SHA2_256, true)),
"SHA-512-256" => Ok(Algorithm::new(AlgorithmType::SHA2_512_256, false)),
"SHA-512-256-sess" => Ok(Algorithm::new(AlgorithmType::SHA2_512_256, true)),
_ => Err(UnknownAlgorithm(s.into())),
}
}
}
impl Default for Algorithm {
/// Get a MD5 instance
fn default() -> Self {
Algorithm::new(AlgorithmType::MD5, false)
}
}
impl Display for Algorithm {
/// Format to the form used in HTTP headers
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str(match self.algo {
AlgorithmType::MD5 => "MD5",
AlgorithmType::SHA2_256 => "SHA-256",
AlgorithmType::SHA2_512_256 => "SHA-512-256",
})?;
if self.sess {
f.write_str("-sess")?;
}
Ok(())
}
}
/// QOP field values
#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types)]
pub enum Qop {
AUTH,
AUTH_INT,
}
impl FromStr for Qop {
type Err = Error;
/// Parse from "auth" or "auth-int" as used in HTTP headers
fn from_str(s: &str) -> Result<Self> {
match s {
"auth" => Ok(Qop::AUTH),
"auth-int" => Ok(Qop::AUTH_INT),
_ => Err(BadQop(s.into())),
}
}
}
impl Display for Qop {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Qop::AUTH => "auth",
Qop::AUTH_INT => "auth-int",
})
}
}
#[derive(Debug)]
#[allow(non_camel_case_types)]
pub enum QopAlgo<'a> {
NONE,
AUTH,
AUTH_INT(&'a [u8]),
}
// casting back...
impl<'a> Into<Option<Qop>> for QopAlgo<'a> {
/// Convert to ?Qop
fn into(self) -> Option<Qop> {
match self {
QopAlgo::NONE => None,
QopAlgo::AUTH => Some(Qop::AUTH),
QopAlgo::AUTH_INT(_) => Some(Qop::AUTH_INT),
}
}
}
/// Charset field value as specified by the server
#[derive(Debug, PartialEq)]
pub enum Charset {
ASCII,
UTF8,
}
impl FromStr for Charset {
type Err = Error;
/// Parse from string (only UTF-8 supported, as prescribed by the specification)
fn from_str(s: &str) -> Result<Self> {
match s {
"UTF-8" => Ok(Charset::UTF8),
_ => Err(BadCharset(s.into())),
}
}
}
/// HTTP method (used when generating the response hash for some Qop options)
#[derive(Debug)]
pub enum HttpMethod {
GET,
POST,
HEAD,
OTHER(&'static str),
}
impl Default for HttpMethod {
fn default() -> Self {
HttpMethod::GET
}
}
impl Display for HttpMethod {
/// Convert to uppercase string
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str(match self {
HttpMethod::GET => "GET",
HttpMethod::POST => "POST",
HttpMethod::HEAD => "HEAD",
HttpMethod::OTHER(s) => s,
})
}
}

@ -0,0 +1,31 @@
use std::fmt::{self, Display, Formatter};
use std::result;
#[derive(Debug)]
pub enum Error {
BadCharset(String),
UnknownAlgorithm(String),
BadQop(String),
MissingRealm(String),
MissingNonce(String),
InvalidHeaderSyntax(String),
BadQopOptions(String),
}
pub type Result<T> = result::Result<T, Error>;
use Error::*;
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
BadCharset(ctx) => write!(f, "Bad charset: {}", ctx),
UnknownAlgorithm(ctx) => write!(f, "Unknown algorithm: {}", ctx),
BadQop(ctx) => write!(f, "Bad Qop option: {}", ctx),
MissingRealm(ctx) => write!(f, "Missing 'realm' in WWW-Authenticate: {}", ctx),
MissingNonce(ctx) => write!(f, "Missing 'nonce' in WWW-Authenticate: {}", ctx),
InvalidHeaderSyntax(ctx) => write!(f, "Invalid header syntax: {}", ctx),
BadQopOptions(ctx) => write!(f, "Illegal Qop in prompt: {}", ctx),
}
}
}

@ -42,19 +42,19 @@
//! assert_eq!(answer2, r#"Digest username="Mufasa", realm="http-auth@example.org", nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", uri="/dir/index.html", qop=auth, nc=00000002, cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", response="4b5d595ecf2db9df612ea5b45cd97101", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS", algorithm=MD5"#); //! assert_eq!(answer2, r#"Digest username="Mufasa", realm="http-auth@example.org", nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", uri="/dir/index.html", qop=auth, nc=00000002, cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", response="4b5d595ecf2db9df612ea5b45cd97101", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS", algorithm=MD5"#);
//! ``` //! ```
#[macro_use] extern crate failure;
use failure::Fallible;
mod digest; mod digest;
mod utils; mod enums;
mod error;
pub use error::{Error, Result};
pub use crate::digest::{AuthContext, AuthorizationHeader, WwwAuthenticateHeader};
pub use crate::digest::{ pub use crate::enums::*;
Algorithm, AuthContext, AuthorizationHeader, HttpMethod, Qop, WwwAuthenticateHeader,
};
/// Parse the WWW-Authorization header value. /// Parse the WWW-Authorization header value.
/// It's just a convenience method to call [`WwwAuthenticateHeader::parse()`](struct.WwwAuthenticateHeader.html#method.parse). /// It's just a convenience method to call [`WwwAuthenticateHeader::parse()`](struct.WwwAuthenticateHeader.html#method.parse).
pub fn parse(www_authorize : &str) -> Fallible<WwwAuthenticateHeader> { pub fn parse(www_authorize: &str) -> Result<WwwAuthenticateHeader> {
WwwAuthenticateHeader::parse(www_authorize) WwwAuthenticateHeader::parse(www_authorize)
} }

@ -1,18 +0,0 @@
use std::string::ToString;
/// slash quoting for digest strings
pub trait QuoteForDigest {
fn quote_for_digest(&self) -> String;
}
impl QuoteForDigest for &str {
fn quote_for_digest(&self) -> String {
self.to_string().quote_for_digest()
}
}
impl QuoteForDigest for String {
fn quote_for_digest(&self) -> String {
self.replace("\\", "\\\\").replace("\"", "\\\"")
}
}
Loading…
Cancel
Save