Joe Koop 3 years ago
parent
commit
1a69f4c158
No known key found for this signature in database
GPG Key ID: B2D0C6242D5AC1FF
  1. 9
      src/args.rs
  2. 55
      src/auth.rs
  3. 4
      src/server.rs
  4. 76
      tests/auth.rs

9
src/args.rs

@ -47,6 +47,12 @@ fn app() -> Command<'static> { @@ -47,6 +47,12 @@ fn app() -> Command<'static> {
.value_name("path")
.help("Specify an url path prefix"),
)
.arg(
Arg::new("basic-auth")
.short('B')
.long("basic-auth")
.help("Use HTTP basic auth instead of digest auth"),
)
.arg(
Arg::new("auth")
.short('a')
@ -123,6 +129,7 @@ pub struct Args { @@ -123,6 +129,7 @@ pub struct Args {
pub path_is_file: bool,
pub path_prefix: String,
pub uri_prefix: String,
pub basic_auth: bool,
pub auth: AccessControl,
pub allow_upload: bool,
pub allow_delete: bool,
@ -162,6 +169,7 @@ impl Args { @@ -162,6 +169,7 @@ impl Args {
.values_of("auth")
.map(|v| v.collect())
.unwrap_or_default();
let basic_auth = matches.is_present("basic-auth");
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");
@ -185,6 +193,7 @@ impl Args { @@ -185,6 +193,7 @@ impl Args {
path_is_file,
path_prefix,
uri_prefix,
basic_auth,
auth,
enable_cors,
allow_delete,

55
src/auth.rs

@ -76,6 +76,7 @@ impl AccessControl { @@ -76,6 +76,7 @@ impl AccessControl {
path: &str,
method: &Method,
authorization: Option<&HeaderValue>,
basic_auth: bool,
) -> GuardType {
if self.rules.is_empty() {
return GuardType::ReadWrite;
@ -86,8 +87,14 @@ impl AccessControl { @@ -86,8 +87,14 @@ impl AccessControl {
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() {
return GuardType::ReadWrite;
if basic_auth {
if valid_basic_auth(authorization, user, pass).is_some() {
return GuardType::ReadWrite;
}
} else {
if valid_digest(authorization, method.as_str(), user, pass).is_some() {
return GuardType::ReadWrite;
}
}
}
}
@ -167,14 +174,42 @@ impl Account { @@ -167,14 +174,42 @@ impl Account {
}
}
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
)
pub fn generate_www_auth(stale: bool, basic_auth: bool) -> String {
if basic_auth {
format!("Basic realm=\"{}\"", REALM)
} else {
let str_stale = if stale { "stale=true," } else { "" };
format!(
"Digest realm=\"{}\",nonce=\"{}\",{}qop=\"auth\"",
REALM,
create_nonce(),
str_stale
)
}
}
pub fn valid_basic_auth(
authorization: &HeaderValue,
auth_user: &str,
auth_pass: &str,
) -> Option<()> {
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(());
}
return None;
}
pub fn valid_digest(

4
src/server.rs

@ -96,7 +96,7 @@ impl Server { @@ -96,7 +96,7 @@ impl Server {
}
let authorization = headers.get(AUTHORIZATION);
let guard_type = self.args.auth.guard(req_path, &method, authorization);
let guard_type = self.args.auth.guard(req_path, &method, authorization, self.args.basic_auth);
if guard_type.is_reject() {
self.auth_reject(&mut res);
return Ok(res);
@ -720,7 +720,7 @@ const DATA = @@ -720,7 +720,7 @@ const DATA =
}
fn auth_reject(&self, res: &mut Response) {
let value = generate_www_auth(false);
let value = generate_www_auth(false, self.args.basic_auth);
set_webdav_headers(res);
res.headers_mut().typed_insert(Connection::close());
res.headers_mut()

76
tests/auth.rs

@ -80,3 +80,79 @@ fn auth_nest_share( @@ -80,3 +80,79 @@ fn auth_nest_share(
assert_eq!(resp.status(), 200);
Ok(())
}
#[rstest]
fn no_auth(#[with(&["--basic-auth", "--auth", "/@user:pass", "-A"])] server: TestServer) -> Result<(), Error> {
let resp = reqwest::blocking::get(server.url())?;
assert_eq!(resp.status(), 401);
assert!(resp.headers().contains_key("www-authenticate"));
let url = format!("{}file1", server.url());
let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?;
assert_eq!(resp.status(), 401);
Ok(())
}
#[rstest]
fn auth(#[with(&["--basic-auth", "--auth", "/@user:pass", "-A"])] server: TestServer) -> Result<(), Error> {
let url = format!("{}file1", server.url());
let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?;
assert_eq!(resp.status(), 401);
let resp = fetch!(b"PUT", &url)
.body(b"abc".to_vec())
.send_with_basic_auth("user", "pass")?;
assert_eq!(resp.status(), 201);
Ok(())
}
#[rstest]
fn auth_skip(#[with(&["--basic-auth", "--auth", "/@user:pass@*"])] server: TestServer) -> Result<(), Error> {
let resp = reqwest::blocking::get(server.url())?;
assert_eq!(resp.status(), 200);
Ok(())
}
#[rstest]
fn auth_readonly(
#[with(&["--basic-auth", "--auth", "/@user:pass@user2:pass2", "-A"])] server: TestServer,
) -> Result<(), Error> {
let url = format!("{}index.html", server.url());
let resp = fetch!(b"GET", &url).send()?;
assert_eq!(resp.status(), 401);
let resp = fetch!(b"GET", &url).send_with_basic_auth("user2", "pass2")?;
assert_eq!(resp.status(), 200);
let url = format!("{}file1", server.url());
let resp = fetch!(b"PUT", &url)
.body(b"abc".to_vec())
.send_with_basic_auth("user2", "pass2")?;
assert_eq!(resp.status(), 401);
Ok(())
}
#[rstest]
fn auth_nest(
#[with(&["--basic-auth", "--auth", "/@user:pass@user2:pass2", "--auth", "/dira@user3:pass3", "-A"])]
server: TestServer,
) -> Result<(), Error> {
let url = format!("{}dira/file1", server.url());
let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?;
assert_eq!(resp.status(), 401);
let resp = fetch!(b"PUT", &url)
.body(b"abc".to_vec())
.send_with_basic_auth("user3", "pass3")?;
assert_eq!(resp.status(), 201);
let resp = fetch!(b"PUT", &url)
.body(b"abc".to_vec())
.send_with_basic_auth("user", "pass")?;
assert_eq!(resp.status(), 201);
Ok(())
}
#[rstest]
fn auth_nest_share(
#[with(&["--basic-auth", "--auth", "/@user:pass@*", "--auth", "/dira@user3:pass3", "-A"])] server: TestServer,
) -> Result<(), Error> {
let url = format!("{}index.html", server.url());
let resp = fetch!(b"GET", &url).send()?;
assert_eq!(resp.status(), 200);
Ok(())
}

Loading…
Cancel
Save