forked from packages/digest_auth_rs
commit
ed6b882fa0
@ -0,0 +1,5 @@ |
|||||||
|
/target |
||||||
|
**/*.rs.bk |
||||||
|
Cargo.lock |
||||||
|
.idea |
||||||
|
|
@ -0,0 +1,11 @@ |
|||||||
|
[package] |
||||||
|
name = "digest_auth" |
||||||
|
version = "0.1.0" |
||||||
|
authors = ["Ondřej Hruška <ondra@ondrovo.com>"] |
||||||
|
edition = "2018" |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
rust-crypto = "0.2" |
||||||
|
rand = "0.6" |
||||||
|
hex = "0.3.2" |
||||||
|
failure = "0.1.5" |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,99 @@ |
|||||||
|
//! This crate implements Digest Auth headers as specified by IETF RFCs 2069, 2617, and 7616.
|
||||||
|
//! It can be used in conjunction with libraries like reqwest to access e.g. IP cameras
|
||||||
|
//! that use this authentication scheme.
|
||||||
|
//!
|
||||||
|
//! This library is intended for the http client. The algorithm is symmetrical,
|
||||||
|
//! it's just not optimized for / tested on the server side yet.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! Basic usage:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use digest_auth::AuthContext;
|
||||||
|
//!
|
||||||
|
//! // Value from the WWW-Authenticate HTTP header (usually in a HTTP 401 response)
|
||||||
|
//! let www_authenticate = r#"Digest realm="http-auth@example.org", qop="auth, auth-int", algorithm=MD5, nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS""#;
|
||||||
|
//!
|
||||||
|
//! // Prepare an authorization context. Note that this is a GET request. There are different
|
||||||
|
//! // constructors available for POST or other request types. You can re-use it, but
|
||||||
|
//! // it's cheap to create a fresh one each time, as the struct uses references only.
|
||||||
|
//! let mut context = AuthContext::new("Mufasa", "Circle of Life", "/dir/index.html");
|
||||||
|
//! // For this test, we inject a custom cnonce. It's generated for you otherwise
|
||||||
|
//! // - you don't need `mut` in that case and needn't worry about this at all.
|
||||||
|
//! context.set_custom_cnonce("f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ");
|
||||||
|
//!
|
||||||
|
//! // Parse the prompt header. You can inspect the parsed object, its fields are public.
|
||||||
|
//! let mut prompt = digest_auth::parse(www_authenticate).unwrap();
|
||||||
|
//!
|
||||||
|
//! // Compute a value for the Authorization header that we'll send back to the server
|
||||||
|
//! let answer = prompt.respond(&context).unwrap().to_string();
|
||||||
|
//! assert_eq!(answer, r#"Digest username="Mufasa", realm="http-auth@example.org", nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", uri="/dir/index.html", qop=auth, nc=00000001, cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", response="8ca523f5e9506fed4657c9700eebdbec", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS", algorithm=MD5"#);
|
||||||
|
//!
|
||||||
|
//! // The `prompt` variable is mutable, because the 'nc' counter (nonce reuse count)
|
||||||
|
//! // is inside the struct and updated automatically.
|
||||||
|
//!
|
||||||
|
//! // You can re-use it for subsequent requests, assuming the server allows nonce re-use.
|
||||||
|
//! // Some poorly implemented servers will reject it and give you 401 again, in which case
|
||||||
|
//! // you should parse the new "WWW-Authenticate" header and use that instead.
|
||||||
|
//!
|
||||||
|
//! let answer2 = prompt.respond(&context).unwrap().to_string();
|
||||||
|
//! // notice how the 'response' field changed - the 'nc' counter is included in the hash
|
||||||
|
//! 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 utils; |
||||||
|
|
||||||
|
use std::str::FromStr; |
||||||
|
|
||||||
|
pub use crate::digest::{ |
||||||
|
Algorithm, AuthContext, AuthorizationHeader, HttpMethod, Qop, WwwAuthenticateHeader, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Parse the WWW-Authorization header value.
|
||||||
|
/// It's just a convenience method to call `WwwAuthenticateHeader::from_str()`.
|
||||||
|
pub fn parse(www_authorize : &str) -> Fallible<WwwAuthenticateHeader> { |
||||||
|
WwwAuthenticateHeader::from_str(www_authorize) |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_parse_respond() { |
||||||
|
let src = r#" |
||||||
|
Digest |
||||||
|
realm="http-auth@example.org", |
||||||
|
qop="auth, auth-int", |
||||||
|
algorithm=MD5, |
||||||
|
nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", |
||||||
|
opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS" |
||||||
|
"#; |
||||||
|
|
||||||
|
let mut context = AuthContext::new("Mufasa", "Circle of Life", "/dir/index.html"); |
||||||
|
context.set_custom_cnonce("f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ"); |
||||||
|
|
||||||
|
let mut prompt = crate::parse(src).unwrap(); |
||||||
|
let answer = prompt.respond(&context).unwrap(); |
||||||
|
|
||||||
|
let str = answer.to_string().replace(", ", ",\n "); |
||||||
|
|
||||||
|
assert_eq!( |
||||||
|
str, |
||||||
|
r#" |
||||||
|
Digest username="Mufasa", |
||||||
|
realm="http-auth@example.org", |
||||||
|
nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", |
||||||
|
uri="/dir/index.html", |
||||||
|
qop=auth, |
||||||
|
nc=00000001, |
||||||
|
cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", |
||||||
|
response="8ca523f5e9506fed4657c9700eebdbec", |
||||||
|
opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS", |
||||||
|
algorithm=MD5 |
||||||
|
"# |
||||||
|
.trim() |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
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…
Reference in new issue