]> OzVa Git service - ozva-cloud/commitdiff
feat: password can contain `:` `@` `|` (#297)
authorsigoden <sigoden@gmail.com>
Sun, 26 Nov 2023 12:47:57 +0000 (20:47 +0800)
committerGitHub <noreply@github.com>
Sun, 26 Nov 2023 12:47:57 +0000 (20:47 +0800)
README.md
src/args.rs
src/auth.rs
tests/auth.rs

index bdf1d059e9f601247b29acbfe3d2e28eeb0ccaac..18f61d8bddbd4ccdd40ddeea057c32586f3b32cc 100644 (file)
--- a/README.md
+++ b/README.md
@@ -81,7 +81,7 @@ Options:
 
 ## Examples
 
-Serve current working directory in readonly mode
+Serve current working directory in read-only mode
 
 ```
 dufs
@@ -206,46 +206,20 @@ curl http://192.168.8.10:5000/file --user user:pass --digest        # digest aut
 Dufs supports account based access control. You can control who can do what on which path with `--auth`/`-a`.
 
 ```
-dufs -a user:pass@path1:rw,path2|user2:pass2@path1
-dufs -a user:pass@path1:rw,path2 -a user2:pass2@path1
+dufs -a user:pass@/path1:rw,/path2 -a user2:pass2@/path3 -a @/path4
 ```
 
-1. Multiple rules are separated by "|"
-2. User and pass are the account name and password, if omitted, it is an anonymous user
-3. One rule can set multiple paths, separated by ","
-4. Add `:rw` after the path to indicate that the path has read and write permissions, otherwise the path has readonly permissions.
+1. Use `@` to separate the account and paths. No account means anonymous user.
+2. Use `:` to separate the username and password of the account.
+3. Use `,` to separate paths.
+4. Use `:rw` suffix to indicate that the account has read-write permission on the path.
 
-```
-dufs -A -a admin:admin@/:rw
-```
-`admin` has all permissions for all paths.
-
-```
-dufs -A -a admin:admin@/:rw -a guest:guest@/
-```
-`guest` has readonly permissions for all paths.
-
-```
-dufs -A -a admin:admin@/:rw -a @/
-```
-All paths is public, everyone can view/download it.
+- `-a admin:amdin@/:rw`: `admin` has complete permissions for all paths.
+- `-a guest:guest@/`: `guest` has read-only permissions for all paths.
+- `-a user:pass@/dir1:rw,/dir2`: `user` has complete permissions for `/dir1/*`, has read-only permissions for `/dir2/`.
+- `-a @/`: All paths is publicly accessible, everyone can view/download it.
 
-```
-dufs -A -a admin:admin@/:rw -a user1:pass1@/user1:rw -a user2:pass2@/user2
-dufs -A -a "admin:admin@/:rw|user1:pass1@/user1:rw|user2:pass2@/user2"
-```
-`user1` has all permissions for `/user1/*` path.
-`user2` has all permissions for `/user2/*` path.
-
-```
-dufs -A -a user:pass@/dir1:rw,/dir2:rw,dir3
-```
-`user` has all permissions for `/dir1/*` and `/dir2/*`, has readonly permissions for `/dir3/`.
-
-```
-dufs -A -a admin:admin@/
-```
-Since dufs only allows viewing/downloading, `admin` can only view/download files.
+> There are no restrictions on using ':' and '@' characters in a password, `user:pa:ss@1@/:rw` is valid, and the password is `pa:ss@1`.
 
 #### Hashed Password
 
@@ -261,13 +235,14 @@ $6$qCAVUG7yn7t/hH4d$BWm8r5MoDywNmDP/J3V2S2a6flmKHC1IpblfoqZfuK.LtLBZ0KFXP9QIfJP8
 
 Use hashed password
 ```
-dufs -A -a 'admin:$6$qCAVUG7yn7t/hH4d$BWm8r5MoDywNmDP/J3V2S2a6flmKHC1IpblfoqZfuK.LtLBZ0KFXP9QIfJP8RqL8MCw4isdheoAMTuwOz.pAO/@/:rw'
+dufs \
+  -a 'admin:$6$qCAVUG7yn7t/hH4d$BWm8r5MoDywNmDP/J3V2S2a6flmKHC1IpblfoqZfuK.LtLBZ0KFXP9QIfJP8RqL8MCw4isdheoAMTuwOz.pAO/@/:rw'
 ```
 
 Two important things for hashed passwords:
 
-1. Dufs only supports SHA-512 hashed passwords, so ensure that the password string always starts with `$6$`.
-2. Digest auth does not work with hashed passwords.
+1. Dufs only supports sha-512 hashed passwords, so ensure that the password string always starts with `$6$`.
+2. Digest authentication does not function properly with hashed passwords.
 
 
 ### Hide Paths
index c7b00a475b3f24ff860b1252dfad009e1b8b8ef0..79bf0c250ceb87705c10b4e8b1751dff09a1d847 100644 (file)
@@ -81,7 +81,6 @@ pub fn build_cli() -> Command {
                 .long("auth")
                 .help("Add auth roles, e.g. user:pass@/dir1:rw,/dir2")
                 .action(ArgAction::Append)
-                .value_delimiter('|')
                 .value_name("rules"),
         )
         .arg(
index b7f57d250dc09542029e007d80d865b92b8b7959..144446b4ed021681f88b8d4ceae829cc85219d89 100644 (file)
@@ -53,7 +53,7 @@ impl AccessControl {
         let mut anony_paths = vec![];
         let mut users = IndexMap::new();
         for rule in raw_rules {
-            let (user, list) = rule.split_once('@').ok_or_else(|| create_err(rule))?;
+            let (user, list) = split_rule(rule).ok_or_else(|| create_err(rule))?;
             if user.is_empty() && anony.is_some() {
                 bail!("Invalid auth, duplicate anonymous rules");
             }
@@ -476,10 +476,25 @@ fn create_nonce() -> Result<String> {
     Ok(n[..34].to_string())
 }
 
+fn split_rule(s: &str) -> Option<(&str, &str)> {
+    let i = s.find("@/")?;
+    Some((&s[0..i], &s[i + 1..]))
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
 
+    #[test]
+    fn test_split_rule() {
+        assert_eq!(split_rule("user:pass@/:rw"), Some(("user:pass", "/:rw")));
+        assert_eq!(split_rule("user:pass@@/:rw"), Some(("user:pass@", "/:rw")));
+        assert_eq!(
+            split_rule("user:pass@1@/:rw"),
+            Some(("user:pass@1", "/:rw"))
+        );
+    }
+
     #[test]
     fn test_access_paths() {
         let mut paths = AccessPaths::default();
index fa206da58586a0420fc56db0a4634b5c10df1a26..a630c650081fb8ddae91d695cb1babad0fb6243b 100644 (file)
@@ -18,13 +18,15 @@ fn no_auth(#[with(&["--auth", "user:pass@/:rw", "-A"])] server: TestServer) -> R
 }
 
 #[rstest]
-fn auth(#[with(&["--auth", "user:pass@/:rw", "-A"])] server: TestServer) -> Result<(), Error> {
+#[case(server(&["--auth", "user:pass@/:rw", "-A"]), "user", "pass")]
+#[case(server(&["--auth", "user:pa:ss@1@/:rw", "-A"]), "user", "pa:ss@1")]
+fn auth(#[case] server: TestServer, #[case] user: &str, #[case] pass: &str) -> 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_digest_auth("user", "pass")?;
+        .send_with_digest_auth(user, pass)?;
     assert_eq!(resp.status(), 201);
     Ok(())
 }
@@ -57,7 +59,7 @@ fn auth_hashed_password(
 
 #[rstest]
 fn auth_and_public(
-    #[with(&["--auth", "user:pass@/:rw|@/", "-A"])] server: TestServer,
+    #[with(&["-a", "user:pass@/:rw", "-a", "@/", "-A"])] server: TestServer,
 ) -> Result<(), Error> {
     let url = format!("{}file1", server.url());
     let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?;
@@ -91,7 +93,7 @@ fn auth_skip_on_options_method(
 
 #[rstest]
 fn auth_check(
-    #[with(&["--auth", "user:pass@/:rw|user2:pass2@/", "-A"])] server: TestServer,
+    #[with(&["--auth", "user:pass@/:rw", "--auth", "user2:pass2@/", "-A"])] server: TestServer,
 ) -> Result<(), Error> {
     let url = format!("{}index.html", server.url());
     let resp = fetch!(b"WRITEABLE", &url).send()?;
@@ -105,7 +107,7 @@ fn auth_check(
 
 #[rstest]
 fn auth_readonly(
-    #[with(&["--auth", "user:pass@/:rw|user2:pass2@/", "-A"])] server: TestServer,
+    #[with(&["--auth", "user:pass@/:rw", "--auth", "user2:pass2@/", "-A"])] server: TestServer,
 ) -> Result<(), Error> {
     let url = format!("{}index.html", server.url());
     let resp = fetch!(b"GET", &url).send()?;
@@ -122,7 +124,7 @@ fn auth_readonly(
 
 #[rstest]
 fn auth_nest(
-    #[with(&["--auth", "user:pass@/:rw|user2:pass2@/", "--auth", "user3:pass3@/dir1:rw", "-A"])]
+    #[with(&["--auth", "user:pass@/:rw", "--auth", "user2:pass2@/", "--auth", "user3:pass3@/dir1:rw", "-A"])]
     server: TestServer,
 ) -> Result<(), Error> {
     let url = format!("{}dir1/file1", server.url());
@@ -242,7 +244,7 @@ fn no_auth_propfind_dir(
 
 #[rstest]
 fn auth_data(
-    #[with(&["--auth", "user:pass@/:rw|@/", "-A"])] server: TestServer,
+    #[with(&["-a", "user:pass@/:rw", "-a", "@/", "-A"])] server: TestServer,
 ) -> Result<(), Error> {
     let resp = reqwest::blocking::get(server.url())?;
     let content = resp.text()?;