Validate WebDAV connections and match directory responses by path
This commit is contained in:
+51
-3
@@ -34,10 +34,11 @@ struct Webdav;
|
||||
|
||||
fn text(setting: Option<Setting>) -> Option<String> {
|
||||
match setting {
|
||||
Some(Setting::Text(value)) | Some(Setting::Secret(value)) => {
|
||||
Some(Setting::Text(value)) => {
|
||||
let value = value.trim().to_owned();
|
||||
(!value.is_empty()).then_some(value)
|
||||
}
|
||||
Some(Setting::Secret(value)) => Some(value),
|
||||
Some(Setting::Toggle(_)) | None => None,
|
||||
}
|
||||
}
|
||||
@@ -225,6 +226,7 @@ impl Guest for Webdav {
|
||||
_provider_id: String,
|
||||
config: Vec<(String, Setting)>,
|
||||
) -> Result<Capabilities, ErrorCode> {
|
||||
SESSION.set(None);
|
||||
let lookup = |wanted: &str| {
|
||||
config
|
||||
.iter()
|
||||
@@ -245,6 +247,17 @@ impl Guest for Webdav {
|
||||
root,
|
||||
authorization,
|
||||
}));
|
||||
let probe = Self::stat(Locator::Path("/".to_owned())).and_then(|info| {
|
||||
if matches!(info.kind, EntryKind::Directory) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorCode::InvalidInput)
|
||||
}
|
||||
});
|
||||
if let Err(error) = probe {
|
||||
SESSION.set(None);
|
||||
return Err(error);
|
||||
}
|
||||
Ok(Capabilities {
|
||||
write: true,
|
||||
mkdir: true,
|
||||
@@ -281,7 +294,8 @@ impl Guest for Webdav {
|
||||
None => 0,
|
||||
};
|
||||
let found = describe(&path, "1")?;
|
||||
let children: Vec<_> = dav::children(&found)?
|
||||
let url = session(|session| Ok(dav::url(&session.root, &path)))?;
|
||||
let children: Vec<_> = dav::children(&found, &url)?
|
||||
.iter()
|
||||
.map(|entry| Entry {
|
||||
name: dav::name_of(&entry.href),
|
||||
@@ -400,7 +414,8 @@ impl Guest for Webdav {
|
||||
/// first and refused unless it is empty.
|
||||
fn remove(target: Locator) -> Result<(), ErrorCode> {
|
||||
let path = route(&target);
|
||||
dav::removable(&describe(&path, "1")?)?;
|
||||
let url = session(|session| Ok(dav::url(&session.root, &path)))?;
|
||||
dav::removable(&describe(&path, "1")?, &url)?;
|
||||
exchange(host::Method::Delete, &path, Vec::new(), &[])?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -411,3 +426,36 @@ impl Guest for Webdav {
|
||||
}
|
||||
|
||||
sdk::export!(Webdav with_types_in sdk);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{SESSION, Session, Webdav, text};
|
||||
use crate::sdk::{ErrorCode, Guest, Setting};
|
||||
|
||||
#[tokio::test]
|
||||
async fn passwords_keep_leading_and_trailing_whitespace() {
|
||||
assert_eq!(
|
||||
text(Some(Setting::Secret(" pass word \t".to_owned()))),
|
||||
Some(" pass word \t".to_owned())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn an_empty_password_still_supplies_basic_auth_credentials() {
|
||||
assert_eq!(
|
||||
text(Some(Setting::Secret(String::new()))),
|
||||
Some(String::new())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_failed_reopen_discards_the_previous_connection() {
|
||||
SESSION.set(Some(Session {
|
||||
root: "https://dav.example/files/".to_owned(),
|
||||
authorization: None,
|
||||
}));
|
||||
let result = Webdav::open("webdav".to_owned(), Vec::new());
|
||||
assert_eq!(result.err(), Some(ErrorCode::InvalidInput));
|
||||
assert!(SESSION.with_borrow(Option::is_none));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user