feat: deprecate the use of |
to separate auth rules (#298)
This commit is contained in:
parent
653cd167d0
commit
7584fe3d08
2 changed files with 86 additions and 18 deletions
90
src/auth.rs
90
src/auth.rs
|
@ -47,38 +47,42 @@ impl AccessControl {
|
||||||
if raw_rules.is_empty() {
|
if raw_rules.is_empty() {
|
||||||
return Ok(Default::default());
|
return Ok(Default::default());
|
||||||
}
|
}
|
||||||
|
let new_raw_rules = compact_split_rules(raw_rules);
|
||||||
|
if new_raw_rules.len() != raw_rules.len() {
|
||||||
|
eprintln!("Warning: deprecate the use of `|` to separate auth rules.")
|
||||||
|
}
|
||||||
let mut use_hashed_password = false;
|
let mut use_hashed_password = false;
|
||||||
let create_err = |v: &str| anyhow!("Invalid auth `{v}`");
|
let create_err = |v: &str| anyhow!("Invalid auth `{v}`");
|
||||||
let mut anony = None;
|
let mut anony = None;
|
||||||
let mut anony_paths = vec![];
|
let mut anony_paths = vec![];
|
||||||
let mut users = IndexMap::new();
|
let mut users = IndexMap::new();
|
||||||
for rule in raw_rules {
|
for rule in &new_raw_rules {
|
||||||
let (user, list) = split_rule(rule).ok_or_else(|| create_err(rule))?;
|
let (account, paths) = split_account_paths(rule).ok_or_else(|| create_err(rule))?;
|
||||||
if user.is_empty() && anony.is_some() {
|
if account.is_empty() && anony.is_some() {
|
||||||
bail!("Invalid auth, duplicate anonymous rules");
|
bail!("Invalid auth, duplicate anonymous rules");
|
||||||
}
|
}
|
||||||
let mut paths = AccessPaths::default();
|
let mut access_paths = AccessPaths::default();
|
||||||
for value in list.trim_matches(',').split(',') {
|
for item in paths.trim_matches(',').split(',') {
|
||||||
let (path, perm) = match value.split_once(':') {
|
let (path, perm) = match item.split_once(':') {
|
||||||
None => (value, AccessPerm::ReadOnly),
|
None => (item, AccessPerm::ReadOnly),
|
||||||
Some((path, "rw")) => (path, AccessPerm::ReadWrite),
|
Some((path, "rw")) => (path, AccessPerm::ReadWrite),
|
||||||
_ => return Err(create_err(rule)),
|
_ => return Err(create_err(rule)),
|
||||||
};
|
};
|
||||||
if user.is_empty() {
|
if account.is_empty() {
|
||||||
anony_paths.push((path, perm));
|
anony_paths.push((path, perm));
|
||||||
}
|
}
|
||||||
paths.add(path, perm);
|
access_paths.add(path, perm);
|
||||||
}
|
}
|
||||||
if user.is_empty() {
|
if account.is_empty() {
|
||||||
anony = Some(paths);
|
anony = Some(access_paths);
|
||||||
} else if let Some((user, pass)) = user.split_once(':') {
|
} else if let Some((user, pass)) = account.split_once(':') {
|
||||||
if user.is_empty() || pass.is_empty() {
|
if user.is_empty() || pass.is_empty() {
|
||||||
return Err(create_err(rule));
|
return Err(create_err(rule));
|
||||||
}
|
}
|
||||||
if pass.starts_with("$6$") {
|
if pass.starts_with("$6$") {
|
||||||
use_hashed_password = true;
|
use_hashed_password = true;
|
||||||
}
|
}
|
||||||
users.insert(user.to_string(), (pass.to_string(), paths));
|
users.insert(user.to_string(), (pass.to_string(), access_paths));
|
||||||
} else {
|
} else {
|
||||||
return Err(create_err(rule));
|
return Err(create_err(rule));
|
||||||
}
|
}
|
||||||
|
@ -476,25 +480,75 @@ fn create_nonce() -> Result<String> {
|
||||||
Ok(n[..34].to_string())
|
Ok(n[..34].to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_rule(s: &str) -> Option<(&str, &str)> {
|
fn split_account_paths(s: &str) -> Option<(&str, &str)> {
|
||||||
let i = s.find("@/")?;
|
let i = s.find("@/")?;
|
||||||
Some((&s[0..i], &s[i + 1..]))
|
Some((&s[0..i], &s[i + 1..]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compatible with deprecated usage of `|` for role separation
|
||||||
|
fn compact_split_rules(rules: &[&str]) -> Vec<String> {
|
||||||
|
let mut output = vec![];
|
||||||
|
for rule in rules {
|
||||||
|
let parts: Vec<&str> = rule.split('|').collect();
|
||||||
|
let mut rules_list = vec![];
|
||||||
|
let mut concated_part = String::new();
|
||||||
|
for (i, part) in parts.iter().enumerate() {
|
||||||
|
if part.contains("@/") {
|
||||||
|
concated_part.push_str(part);
|
||||||
|
let mut concated_part_tmp = String::new();
|
||||||
|
std::mem::swap(&mut concated_part_tmp, &mut concated_part);
|
||||||
|
rules_list.push(concated_part_tmp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
concated_part.push_str(part);
|
||||||
|
if i < parts.len() - 1 {
|
||||||
|
concated_part.push('|');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !concated_part.is_empty() {
|
||||||
|
rules_list.push(concated_part)
|
||||||
|
}
|
||||||
|
output.extend(rules_list);
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_split_rule() {
|
fn test_split_account_paths() {
|
||||||
assert_eq!(split_rule("user:pass@/:rw"), Some(("user:pass", "/:rw")));
|
|
||||||
assert_eq!(split_rule("user:pass@@/:rw"), Some(("user:pass@", "/:rw")));
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
split_rule("user:pass@1@/:rw"),
|
split_account_paths("user:pass@/:rw"),
|
||||||
|
Some(("user:pass", "/:rw"))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
split_account_paths("user:pass@@/:rw"),
|
||||||
|
Some(("user:pass@", "/:rw"))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
split_account_paths("user:pass@1@/:rw"),
|
||||||
Some(("user:pass@1", "/:rw"))
|
Some(("user:pass@1", "/:rw"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compact_split_rules() {
|
||||||
|
assert_eq!(
|
||||||
|
compact_split_rules(&["user1:pass1@/:rw|user2:pass2@/:rw"]),
|
||||||
|
["user1:pass1@/:rw", "user2:pass2@/:rw"]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
compact_split_rules(&["user1:pa|ss1@/:rw|user2:pa|ss2@/:rw"]),
|
||||||
|
["user1:pa|ss1@/:rw", "user2:pa|ss2@/:rw"]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
compact_split_rules(&["user1:pa|ss1@/:rw|@/"]),
|
||||||
|
["user1:pa|ss1@/:rw", "@/"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_access_paths() {
|
fn test_access_paths() {
|
||||||
let mut paths = AccessPaths::default();
|
let mut paths = AccessPaths::default();
|
||||||
|
|
|
@ -105,6 +105,20 @@ fn auth_check(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn auth_compact_rules(
|
||||||
|
#[with(&["--auth", "user:pass@/:rw|user2:pass2@/", "-A"])] server: TestServer,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let url = format!("{}index.html", server.url());
|
||||||
|
let resp = fetch!(b"WRITEABLE", &url).send()?;
|
||||||
|
assert_eq!(resp.status(), 401);
|
||||||
|
let resp = fetch!(b"WRITEABLE", &url).send_with_digest_auth("user2", "pass2")?;
|
||||||
|
assert_eq!(resp.status(), 403);
|
||||||
|
let resp = fetch!(b"WRITEABLE", &url).send_with_digest_auth("user", "pass")?;
|
||||||
|
assert_eq!(resp.status(), 200);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn auth_readonly(
|
fn auth_readonly(
|
||||||
#[with(&["--auth", "user:pass@/:rw", "--auth", "user2:pass2@/", "-A"])] server: TestServer,
|
#[with(&["--auth", "user:pass@/:rw", "--auth", "user2:pass2@/", "-A"])] server: TestServer,
|
||||||
|
|
Loading…
Reference in a new issue