use std::path::{Path, PathBuf};
use crate::auth::AccessControl;
+use crate::auth::AuthMethod;
use crate::tls::{load_certs, load_private_key};
use crate::BoxResult;
.value_name("path")
.help("Specify an url path prefix"),
)
+ .arg(
+ Arg::new("auth-method")
+ .long("auth-method")
+ .help("Choose auth method")
+ .possible_values(["basic", "digest"])
+ .default_value("digest")
+ .value_name("value"),
+ )
.arg(
Arg::new("auth")
.short('a')
pub path_is_file: bool,
pub path_prefix: String,
pub uri_prefix: String,
+ pub auth_method: AuthMethod,
pub auth: AccessControl,
pub allow_upload: bool,
pub allow_delete: bool,
.values_of("auth")
.map(|v| v.collect())
.unwrap_or_default();
+ let auth_method = match matches.value_of("auth-method").unwrap() {
+ "basic" => AuthMethod::Basic,
+ _ => AuthMethod::Digest,
+ };
let auth = AccessControl::new(&auth, &uri_prefix)?;
let allow_upload = matches.is_present("allow-all") || matches.is_present("allow-upload");
let allow_delete = matches.is_present("allow-all") || matches.is_present("allow-delete");
path_is_file,
path_prefix,
uri_prefix,
+ auth_method,
auth,
enable_cors,
allow_delete,
path: &str,
method: &Method,
authorization: Option<&HeaderValue>,
+ auth_method: AuthMethod,
) -> GuardType {
if self.rules.is_empty() {
return GuardType::ReadWrite;
controls.push(control);
if let Some(authorization) = authorization {
let Account { user, pass } = &control.readwrite;
- if valid_digest(authorization, method.as_str(), user, pass).is_some() {
+ if auth_method
+ .validate(authorization, method.as_str(), user, pass)
+ .is_some()
+ {
return GuardType::ReadWrite;
}
}
}
if let Some(authorization) = authorization {
if let Some(Account { user, pass }) = &control.readonly {
- if valid_digest(authorization, method.as_str(), user, pass).is_some() {
+ if auth_method
+ .validate(authorization, method.as_str(), user, pass)
+ .is_some()
+ {
return GuardType::ReadOnly;
}
}
}
}
-pub fn generate_www_auth(stale: bool) -> String {
- let str_stale = if stale { "stale=true," } else { "" };
- format!(
- "Digest realm=\"{}\",nonce=\"{}\",{}qop=\"auth\"",
- REALM,
- create_nonce(),
- str_stale
- )
+#[derive(Debug, Clone)]
+pub enum AuthMethod {
+ Basic,
+ Digest,
}
-pub fn valid_digest(
- authorization: &HeaderValue,
- method: &str,
- auth_user: &str,
- auth_pass: &str,
-) -> Option<()> {
- let digest_value = strip_prefix(authorization.as_bytes(), b"Digest ")?;
- let user_vals = to_headermap(digest_value).ok()?;
- if let (Some(username), Some(nonce), Some(user_response)) = (
- user_vals
- .get(b"username".as_ref())
- .and_then(|b| std::str::from_utf8(*b).ok()),
- user_vals.get(b"nonce".as_ref()),
- user_vals.get(b"response".as_ref()),
- ) {
- match validate_nonce(nonce) {
- Ok(true) => {}
- _ => return None,
- }
- if auth_user != username {
- return None;
- }
- let mut ha = Context::new();
- ha.consume(method);
- ha.consume(b":");
- if let Some(uri) = user_vals.get(b"uri".as_ref()) {
- ha.consume(uri);
+impl AuthMethod {
+ pub fn www_auth(&self, stale: bool) -> String {
+ match self {
+ AuthMethod::Basic => {
+ format!("Basic realm=\"{}\"", REALM)
+ }
+ AuthMethod::Digest => {
+ let str_stale = if stale { "stale=true," } else { "" };
+ format!(
+ "Digest realm=\"{}\",nonce=\"{}\",{}qop=\"auth\"",
+ REALM,
+ create_nonce(),
+ str_stale
+ )
+ }
}
- let ha = format!("{:x}", ha.compute());
- let mut correct_response = None;
- if let Some(qop) = user_vals.get(b"qop".as_ref()) {
- if qop == &b"auth".as_ref() || qop == &b"auth-int".as_ref() {
- correct_response = Some({
- let mut c = Context::new();
- c.consume(&auth_pass);
- c.consume(b":");
- c.consume(nonce);
- c.consume(b":");
- if let Some(nc) = user_vals.get(b"nc".as_ref()) {
- c.consume(nc);
+ }
+ pub fn validate(
+ &self,
+ authorization: &HeaderValue,
+ method: &str,
+ auth_user: &str,
+ auth_pass: &str,
+ ) -> Option<()> {
+ match self {
+ AuthMethod::Basic => {
+ let value: Vec<u8> =
+ base64::decode(strip_prefix(authorization.as_bytes(), b"Basic ").unwrap())
+ .unwrap();
+ let parts: Vec<&str> = std::str::from_utf8(&value).unwrap().split(':').collect();
+
+ if parts[0] != auth_user {
+ return None;
+ }
+
+ let mut h = Context::new();
+ h.consume(format!("{}:{}:{}", parts[0], REALM, parts[1]).as_bytes());
+
+ let http_pass = format!("{:x}", h.compute());
+
+ if http_pass == auth_pass {
+ return Some(());
+ }
+
+ None
+ }
+ AuthMethod::Digest => {
+ let digest_value = strip_prefix(authorization.as_bytes(), b"Digest ")?;
+ let user_vals = to_headermap(digest_value).ok()?;
+ if let (Some(username), Some(nonce), Some(user_response)) = (
+ user_vals
+ .get(b"username".as_ref())
+ .and_then(|b| std::str::from_utf8(*b).ok()),
+ user_vals.get(b"nonce".as_ref()),
+ user_vals.get(b"response".as_ref()),
+ ) {
+ match validate_nonce(nonce) {
+ Ok(true) => {}
+ _ => return None,
}
- c.consume(b":");
- if let Some(cnonce) = user_vals.get(b"cnonce".as_ref()) {
- c.consume(cnonce);
+ if auth_user != username {
+ return None;
}
- c.consume(b":");
- c.consume(qop);
- c.consume(b":");
- c.consume(&*ha);
- format!("{:x}", c.compute())
- });
- }
- }
- let correct_response = match correct_response {
- Some(r) => r,
- None => {
- let mut c = Context::new();
- c.consume(&auth_pass);
- c.consume(b":");
- c.consume(nonce);
- c.consume(b":");
- c.consume(&*ha);
- format!("{:x}", c.compute())
+ let mut ha = Context::new();
+ ha.consume(method);
+ ha.consume(b":");
+ if let Some(uri) = user_vals.get(b"uri".as_ref()) {
+ ha.consume(uri);
+ }
+ let ha = format!("{:x}", ha.compute());
+ let mut correct_response = None;
+ if let Some(qop) = user_vals.get(b"qop".as_ref()) {
+ if qop == &b"auth".as_ref() || qop == &b"auth-int".as_ref() {
+ correct_response = Some({
+ let mut c = Context::new();
+ c.consume(&auth_pass);
+ c.consume(b":");
+ c.consume(nonce);
+ c.consume(b":");
+ if let Some(nc) = user_vals.get(b"nc".as_ref()) {
+ c.consume(nc);
+ }
+ c.consume(b":");
+ if let Some(cnonce) = user_vals.get(b"cnonce".as_ref()) {
+ c.consume(cnonce);
+ }
+ c.consume(b":");
+ c.consume(qop);
+ c.consume(b":");
+ c.consume(&*ha);
+ format!("{:x}", c.compute())
+ });
+ }
+ }
+ let correct_response = match correct_response {
+ Some(r) => r,
+ None => {
+ let mut c = Context::new();
+ c.consume(&auth_pass);
+ c.consume(b":");
+ c.consume(nonce);
+ c.consume(b":");
+ c.consume(&*ha);
+ format!("{:x}", c.compute())
+ }
+ };
+ if correct_response.as_bytes() == *user_response {
+ // grant access
+ return Some(());
+ }
+ }
+ None
}
- };
- if correct_response.as_bytes() == *user_response {
- // grant access
- return Some(());
}
}
- None
}
/// Check if a nonce is still valid.