format code, version bump

pull/4/head
Ondřej Hruška 5 years ago
parent 891c7a183d
commit abda2133bf
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 2
      Cargo.toml
  2. 157
      src/digest.rs

@ -1,6 +1,6 @@
[package]
name = "digest_auth"
version = "0.2.0"
version = "0.2.1"
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
edition = "2018"
description = "Implementation of the Digest Auth algorithm as defined in IETF RFC 2069, 2617, and 7616, intended for HTTP clients"

@ -32,7 +32,7 @@ impl QuoteForDigest for String {
}
/// Join a Vec of Display items using a separator
fn join_vec<T : ToString>(vec : &Vec<T>, sep : &str) -> String {
fn join_vec<T: ToString>(vec: &Vec<T>, sep: &str) -> String {
vec.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
@ -41,7 +41,7 @@ fn join_vec<T : ToString>(vec : &Vec<T>, sep : &str) -> String {
enum NamedTag<'a> {
Quoted(&'a str, Cow<'a, str>),
Plain(&'a str, Cow<'a, str>)
Plain(&'a str, Cow<'a, str>),
}
impl Display for NamedTag<'_> {
@ -50,14 +50,11 @@ impl Display for NamedTag<'_> {
NamedTag::Quoted(name, content) => {
write!(f, "{}=\"{}\"", name, content.quote_for_digest())
}
NamedTag::Plain(name, content) => {
write!(f, "{}={}", name, content)
}
NamedTag::Plain(name, content) => write!(f, "{}={}", name, content),
}
}
}
/// Helper func that parses the key-value string received from server
fn parse_header_map(input: &str) -> Result<HashMap<String, String>> {
#[derive(Debug)]
@ -148,7 +145,6 @@ fn parse_header_map(input: &str) -> Result<HashMap<String, String>> {
Ok(parsed)
}
/// Login attempt context
///
/// All fields are borrowed to reduce runtime overhead; this struct should not be stored anywhere,
@ -174,25 +170,28 @@ impl<'a> AuthContext<'a> {
/// Construct a new context with the GET verb and no payload body.
/// See the other constructors if this does not fit your situation.
pub fn new<UN, PW, UR>(username: UN, password: PW, uri: UR) -> Self
where UN: Into<Cow<'a, str>>,
PW: Into<Cow<'a, str>>,
UR: Into<Cow<'a, str>>
where
UN: Into<Cow<'a, str>>,
PW: Into<Cow<'a, str>>,
UR: Into<Cow<'a, str>>,
{
Self::new_with_method(username, password, uri, Option::<&'a[u8]>::None, HttpMethod::GET)
Self::new_with_method(
username,
password,
uri,
Option::<&'a [u8]>::None,
HttpMethod::GET,
)
}
/// Construct a new context with the POST verb and a payload body (may be None).
/// See the other constructors if this does not fit your situation.
pub fn new_post<UN, PW, UR, BD>(
username: UN,
password: PW,
uri: UR,
body: Option<BD>,
) -> Self
where UN: Into<Cow<'a, str>>,
PW: Into<Cow<'a, str>>,
UR: Into<Cow<'a, str>>,
BD: Into<Cow<'a, [u8]>>,
pub fn new_post<UN, PW, UR, BD>(username: UN, password: PW, uri: UR, body: Option<BD>) -> Self
where
UN: Into<Cow<'a, str>>,
PW: Into<Cow<'a, str>>,
UR: Into<Cow<'a, str>>,
BD: Into<Cow<'a, [u8]>>,
{
Self::new_with_method(username, password, uri, body, HttpMethod::POST)
}
@ -205,16 +204,17 @@ impl<'a> AuthContext<'a> {
body: Option<BD>,
method: HttpMethod,
) -> Self
where UN: Into<Cow<'a, str>>,
PW: Into<Cow<'a, str>>,
UR: Into<Cow<'a, str>>,
BD: Into<Cow<'a, [u8]>>
where
UN: Into<Cow<'a, str>>,
PW: Into<Cow<'a, str>>,
UR: Into<Cow<'a, str>>,
BD: Into<Cow<'a, [u8]>>,
{
Self {
username : username.into(),
password : password.into(),
uri : uri.into(),
body : body.map(Into::into),
username: username.into(),
password: password.into(),
uri: uri.into(),
body: body.map(Into::into),
method,
cnonce: None,
}
@ -223,7 +223,7 @@ impl<'a> AuthContext<'a> {
/// Set cnonce to the given value
pub fn set_custom_cnonce<CN>(&mut self, cnonce: CN)
where
CN: Into<Cow<'a, str>>
CN: Into<Cow<'a, str>>,
{
self.cnonce = Some(cnonce.into());
}
@ -269,10 +269,7 @@ impl FromStr for WwwAuthenticateHeader {
impl WwwAuthenticateHeader {
/// 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.
pub fn respond(
&mut self,
secrets: &AuthContext,
) -> Result<AuthorizationHeader> {
pub fn respond(&mut self, secrets: &AuthContext) -> Result<AuthorizationHeader> {
AuthorizationHeader::from_prompt(self, secrets)
}
@ -357,7 +354,10 @@ impl Display for WwwAuthenticateHeader {
entries.push(NamedTag::Plain("stale", "true".into()));
}
entries.push(NamedTag::Plain("algorithm", self.algorithm.to_string().into()));
entries.push(NamedTag::Plain(
"algorithm",
self.algorithm.to_string().into(),
));
entries.push(NamedTag::Quoted("nonce", (&self.nonce).into()));
if let Some(ref opaque) = self.opaque {
entries.push(NamedTag::Quoted("opaque", (opaque).into()));
@ -369,7 +369,9 @@ impl Display for WwwAuthenticateHeader {
}
for (i, e) in entries.iter().enumerate() {
if i > 0 { f.write_str(", ")?; }
if i > 0 {
f.write_str(", ")?;
}
f.write_str(&e.to_string())?;
}
@ -385,15 +387,15 @@ impl Display for WwwAuthenticateHeader {
#[derive(Debug, PartialEq, Clone)]
pub struct AuthorizationHeader {
/// Authorization realm
pub realm : String,
pub realm: String,
/// Server nonce
pub nonce: String,
/// Server opaque
pub opaque: Option<String>,
/// Flag that userhash was used
pub userhash : bool,
pub userhash: bool,
/// Hash algorithm
pub algorithm : Algorithm,
pub algorithm: Algorithm,
/// Computed digest
pub response: String,
/// Username or hash (owned because of the computed hash)
@ -428,7 +430,6 @@ impl AuthorizationHeader {
prompt: &mut WwwAuthenticateHeader,
context: &AuthContext,
) -> Result<AuthorizationHeader> {
let qop = match &prompt.qop {
None => None,
Some(vec) => {
@ -453,11 +454,15 @@ impl AuthorizationHeader {
opaque: prompt.opaque.clone(),
userhash: prompt.userhash,
algorithm: prompt.algorithm,
response : String::default(),
username : String::default(),
response: String::default(),
username: String::default(),
uri: context.uri.as_ref().into(),
qop,
cnonce: context.cnonce.as_ref().map(AsRef::as_ref).map(ToOwned::to_owned), // Will be generated if needed, if build_hash is set and this is None
cnonce: context
.cnonce
.as_ref()
.map(AsRef::as_ref)
.map(ToOwned::to_owned), // Will be generated if needed, if build_hash is set and this is None
nc: prompt.nc,
};
@ -479,8 +484,7 @@ impl AuthorizationHeader {
/// - cnonce (if it was None before)
/// - username copied from context
/// - response
pub fn digest(&mut self, context : &AuthContext)
{
pub fn digest(&mut self, context: &AuthContext) {
// figure out which QOP option to use
let qop_algo = match self.qop {
None => QopAlgo::NONE,
@ -492,9 +496,7 @@ impl AuthorizationHeader {
QopAlgo::AUTH
}
}
Some(Qop::AUTH) => {
QopAlgo::AUTH
}
Some(Qop::AUTH) => QopAlgo::AUTH,
};
let h = &self.algorithm;
@ -554,7 +556,7 @@ impl AuthorizationHeader {
username = context.username,
realm = self.realm
)
.as_bytes(),
.as_bytes(),
)
} else {
context.username.as_ref().to_owned()
@ -653,7 +655,7 @@ impl AuthorizationHeader {
if auth.qop.is_some() {
if !auth.cnonce.is_some() {
return Err(MissingRequired("cnonce", input.into()))
return Err(MissingRequired("cnonce", input.into()));
}
} else {
// cnonce must not be set if qop is not given, clear it.
@ -676,9 +678,15 @@ impl Display for AuthorizationHeader {
entries.push(NamedTag::Quoted("uri", (&self.uri).into()));
if self.qop.is_some() && self.cnonce.is_some() {
entries.push(NamedTag::Plain("qop", self.qop.as_ref().unwrap().to_string().into()));
entries.push(NamedTag::Plain(
"qop",
self.qop.as_ref().unwrap().to_string().into(),
));
entries.push(NamedTag::Plain("nc", format!("{:08x}", self.nc).into()));
entries.push(NamedTag::Quoted("cnonce", self.cnonce.as_ref().unwrap().into()));
entries.push(NamedTag::Quoted(
"cnonce",
self.cnonce.as_ref().unwrap().into(),
));
}
entries.push(NamedTag::Quoted("response", (&self.response).into()));
@ -689,7 +697,10 @@ impl Display for AuthorizationHeader {
// algorithm can be omitted if it is the default value (or in legacy compat mode)
if self.qop.is_some() || self.algorithm.algo != AlgorithmType::MD5 {
entries.push(NamedTag::Plain("algorithm", self.algorithm.to_string().into()));
entries.push(NamedTag::Plain(
"algorithm",
self.algorithm.to_string().into(),
));
}
if self.userhash {
@ -697,7 +708,9 @@ impl Display for AuthorizationHeader {
}
for (i, e) in entries.iter().enumerate() {
if i > 0 { f.write_str(", ")?; }
if i > 0 {
f.write_str(", ")?;
}
f.write_str(&e.to_string())?;
}
@ -756,8 +769,7 @@ mod tests {
}
#[test]
fn test_parse_header_map2()
{
fn test_parse_header_map2() {
let src = r#"realm="api@example.org""#;
let map = parse_header_map(src).unwrap();
assert_eq!(map.get("realm").unwrap(), "api@example.org");
@ -836,38 +848,47 @@ mod tests {
};
assert_eq!(
r#"Digest realm="api@example.org",
r#"Digest realm="api@example.org",
qop="auth",
domain="/my/nice/url /login /logout",
algorithm=SHA-512-256,
nonce="5TsQWLVdgBdmrQ0XsxbDODV+57QdFR34I9HAbC/RVvkK",
opaque="HRPCssKJSGjCrkzDg8OhwpzCiGPChXYjwrI2QmXDnsOS",
charset=UTF-8,
userhash=true"#.replace(",\n ", ", "), hdr.to_string());
userhash=true"#
.replace(",\n ", ", "),
hdr.to_string()
);
hdr.stale=true;
hdr.userhash=false;
hdr.stale = true;
hdr.userhash = false;
hdr.opaque = None;
hdr.qop = None;
assert_eq!(
r#"Digest realm="api@example.org",
r#"Digest realm="api@example.org",
domain="/my/nice/url /login /logout",
stale=true,
algorithm=SHA-512-256,
nonce="5TsQWLVdgBdmrQ0XsxbDODV+57QdFR34I9HAbC/RVvkK",
charset=UTF-8"#.replace(",\n ", ", "), hdr.to_string());
charset=UTF-8"#
.replace(",\n ", ", "),
hdr.to_string()
);
hdr.qop = Some(vec![Qop::AUTH, Qop::AUTH_INT]);
assert_eq!(
r#"Digest realm="api@example.org",
r#"Digest realm="api@example.org",
qop="auth, auth-int",
domain="/my/nice/url /login /logout",
stale=true,
algorithm=SHA-512-256,
nonce="5TsQWLVdgBdmrQ0XsxbDODV+57QdFR34I9HAbC/RVvkK",
charset=UTF-8"#.replace(",\n ", ", "), hdr.to_string());
charset=UTF-8"#
.replace(",\n ", ", "),
hdr.to_string()
);
}
#[test]
@ -949,7 +970,7 @@ Digest username="Mufasa",
response="1949323746fe6a43ef61f9606e7febea",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
"#
.trim()
.trim()
);
// Try round trip
@ -992,7 +1013,7 @@ Digest username="Mufasa",
opaque="5ccc069c403ebaf9f0171e9517f40e41",
algorithm=MD5
"#
.trim()
.trim()
);
// Try round trip
@ -1033,7 +1054,7 @@ Digest username="Mufasa",
opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS",
algorithm=MD5
"#
.trim()
.trim()
);
// Try round trip
@ -1085,7 +1106,7 @@ Digest username="Mufasa",
opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS",
algorithm=SHA-256
"#
.trim()
.trim()
);
// Try round trip

Loading…
Cancel
Save