mod fixtures; mod utils; use diqwest::blocking::WithDigestAuth; use fixtures::{server, Error, TestServer}; use indexmap::IndexSet; use rstest::rstest; #[rstest] fn no_auth(#[with(&["--auth", "user:pass@/:rw", "-A"])] server: TestServer) -> Result<(), Error> { let resp = reqwest::blocking::get(server.url())?; assert_eq!(resp.status(), 401); let values: Vec<&str> = resp .headers() .get_all("www-authenticate") .iter() .map(|v| v.to_str().unwrap()) .collect(); assert!(values[0].starts_with("Digest")); assert!(values[1].starts_with("Basic")); 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] #[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)?; assert_eq!(resp.status(), 201); Ok(()) } const HASHED_PASSWORD_AUTH: &str = "user:$6$gQxZwKyWn/ZmWEA2$4uV7KKMnSUnET2BtWTj/9T5.Jq3h/MdkOlnIl5hdlTxDZ4MZKmJ.kl6C.NL9xnNPqC4lVHC1vuI0E5cLpTJX81@/:rw"; // user:pass #[rstest] fn auth_hashed_password( #[with(&["--auth", HASHED_PASSWORD_AUTH, "-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); if let Err(err) = fetch!(b"PUT", &url) .body(b"abc".to_vec()) .send_with_digest_auth("user", "pass") { assert_eq!( format!("{err:?}"), r#"DigestAuth(MissingRequired("realm", "Basic realm=\"DUFS\""))"# ); } let resp = fetch!(b"PUT", &url) .body(b"abc".to_vec()) .basic_auth("user", Some("pass")) .send()?; assert_eq!(resp.status(), 201); Ok(()) } #[rstest] fn auth_and_public( #[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()?; assert_eq!(resp.status(), 401); let resp = fetch!(b"PUT", &url) .body(b"abc".to_vec()) .send_with_digest_auth("user", "pass")?; assert_eq!(resp.status(), 201); let resp = fetch!(b"GET", &url).send()?; assert_eq!(resp.status(), 200); assert_eq!(resp.text()?, "abc"); Ok(()) } #[rstest] fn auth_skip(#[with(&["--auth", "@/"])] server: TestServer) -> Result<(), Error> { let resp = reqwest::blocking::get(server.url())?; assert_eq!(resp.status(), 200); Ok(()) } #[rstest] fn auth_skip_on_options_method( #[with(&["--auth", "user:pass@/:rw"])] server: TestServer, ) -> Result<(), Error> { let url = format!("{}index.html", server.url()); let resp = fetch!(b"OPTIONS", &url).send()?; assert_eq!(resp.status(), 200); Ok(()) } #[rstest] fn auth_check( #[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()?; 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] 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] fn auth_readonly( #[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()?; assert_eq!(resp.status(), 401); let resp = fetch!(b"GET", &url).send_with_digest_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_digest_auth("user2", "pass2")?; assert_eq!(resp.status(), 403); Ok(()) } #[rstest] fn auth_forbidden( #[with(&["--auth", "user:pass@/:rw,/dir1:-", "-A"])] server: TestServer, ) -> Result<(), Error> { let url = format!("{}file1", server.url()); let resp = fetch!(b"PUT", &url) .body(b"abc".to_vec()) .send_with_digest_auth("user", "pass")?; assert_eq!(resp.status(), 201); let url = format!("{}dir1/file1", server.url()); let resp = fetch!(b"PUT", &url) .body(b"abc".to_vec()) .send_with_digest_auth("user", "pass")?; assert_eq!(resp.status(), 403); Ok(()) } #[rstest] fn auth_nest( #[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()); 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("user3", "pass3")?; assert_eq!(resp.status(), 201); let resp = fetch!(b"PUT", &url) .body(b"abc".to_vec()) .send_with_digest_auth("user", "pass")?; assert_eq!(resp.status(), 201); Ok(()) } #[rstest] fn auth_nest_share( #[with(&["--auth", "@/", "--auth", "user:pass@/:rw", "--auth", "user3:pass3@/dir1:rw", "-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(()) } #[rstest] #[case(server(&["--auth", "user:pass@/:rw", "-A"]), "user", "pass")] #[case(server(&["--auth", "u1:p1@/:rw", "-A"]), "u1", "p1")] fn auth_basic( #[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()) .basic_auth(user, Some(pass)) .send()?; assert_eq!(resp.status(), 201); Ok(()) } #[rstest] fn auth_webdav_move( #[with(&["--auth", "user:pass@/:rw", "--auth", "user3:pass3@/dir1:rw", "-A"])] server: TestServer, ) -> Result<(), Error> { let origin_url = format!("{}dir1/test.html", server.url()); let new_url = format!("{}test2.html", server.url()); let resp = fetch!(b"MOVE", &origin_url) .header("Destination", &new_url) .send_with_digest_auth("user3", "pass3")?; assert_eq!(resp.status(), 403); Ok(()) } #[rstest] fn auth_webdav_copy( #[with(&["--auth", "user:pass@/:rw", "--auth", "user3:pass3@/dir1:rw", "-A"])] server: TestServer, ) -> Result<(), Error> { let origin_url = format!("{}dir1/test.html", server.url()); let new_url = format!("{}test2.html", server.url()); let resp = fetch!(b"COPY", &origin_url) .header("Destination", &new_url) .send_with_digest_auth("user3", "pass3")?; assert_eq!(resp.status(), 403); Ok(()) } #[rstest] fn auth_path_prefix( #[with(&["--auth", "user:pass@/:rw", "--path-prefix", "xyz", "-A"])] server: TestServer, ) -> Result<(), Error> { let url = format!("{}xyz/index.html", server.url()); let resp = fetch!(b"GET", &url).send()?; assert_eq!(resp.status(), 401); let resp = fetch!(b"GET", &url).send_with_digest_auth("user", "pass")?; assert_eq!(resp.status(), 200); Ok(()) } #[rstest] fn auth_partial_index( #[with(&["--auth", "user:pass@/dir1:rw,/dir2:rw", "-A"])] server: TestServer, ) -> Result<(), Error> { let resp = fetch!(b"GET", server.url()).send_with_digest_auth("user", "pass")?; assert_eq!(resp.status(), 200); let paths = utils::retrieve_index_paths(&resp.text()?); assert_eq!(paths, IndexSet::from(["dir1/".into(), "dir2/".into()])); let resp = fetch!(b"GET", format!("{}?q={}", server.url(), "test.html")) .send_with_digest_auth("user", "pass")?; assert_eq!(resp.status(), 200); let paths = utils::retrieve_index_paths(&resp.text()?); assert_eq!( paths, IndexSet::from(["dir1/test.html".into(), "dir2/test.html".into()]) ); Ok(()) } #[rstest] fn no_auth_propfind_dir( #[with(&["--auth", "admin:admin@/:rw", "--auth", "@/dir-assets", "-A"])] server: TestServer, ) -> Result<(), Error> { let resp = fetch!(b"PROPFIND", server.url()).send()?; assert_eq!(resp.status(), 207); let body = resp.text()?; assert!(body.contains("/dir-assets/")); assert!(body.contains("/dir1/")); Ok(()) } #[rstest] fn auth_propfind_dir( #[with(&["--auth", "admin:admin@/:rw", "--auth", "user:pass@/dir-assets", "-A"])] server: TestServer, ) -> Result<(), Error> { let resp = fetch!(b"PROPFIND", server.url()).send_with_digest_auth("user", "pass")?; assert_eq!(resp.status(), 207); let body = resp.text()?; assert!(body.contains("/dir-assets/")); assert!(!body.contains("/dir1/")); Ok(()) } #[rstest] fn auth_data( #[with(&["-a", "user:pass@/:rw", "-a", "@/", "-A"])] server: TestServer, ) -> Result<(), Error> { let resp = reqwest::blocking::get(server.url())?; let content = resp.text()?; let json = utils::retrive_json(&content).unwrap(); assert_eq!(json["allow_delete"], serde_json::Value::Bool(false)); assert_eq!(json["allow_upload"], serde_json::Value::Bool(false)); let resp = fetch!(b"GET", server.url()) .basic_auth("user", Some("pass")) .send()?; let content = resp.text()?; let json = utils::retrive_json(&content).unwrap(); assert_eq!(json["allow_delete"], serde_json::Value::Bool(true)); assert_eq!(json["allow_upload"], serde_json::Value::Bool(true)); Ok(()) } #[rstest] fn auth_precedence( #[with(&["--auth", "user:pass@/dir1:rw,/dir1/test.txt", "-A"])] server: TestServer, ) -> Result<(), Error> { let url = format!("{}dir1/test.txt", server.url()); let resp = fetch!(b"PUT", &url) .body(b"abc".to_vec()) .send_with_digest_auth("user", "pass")?; assert_eq!(resp.status(), 403); let url = format!("{}dir1/file1", server.url()); let resp = fetch!(b"PUT", &url) .body(b"abc".to_vec()) .send_with_digest_auth("user", "pass")?; assert_eq!(resp.status(), 201); Ok(()) }