#![cfg(windows)]
use persistant_relational::{RelationalBackend, Value};
use persistant_sqlx::SqliteBackend;
use std::path::Path;
use std::process::Command;
fn scratch(name: &str) -> std::path::PathBuf {
let dir = std::env::temp_dir().join(format!("persistant-acl-{name}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
dir
}
fn run(cmd: &str, args: &[&str]) -> String {
let out = Command::new(cmd).args(args).output().unwrap();
assert!(out.status.success(), "{cmd} {args:?}: {out:?}");
String::from_utf8_lossy(&out.stdout).to_string()
}
fn me() -> String {
run("whoami", &[]).trim().to_lowercase()
}
fn entries(path: &Path) -> Vec<String> {
let text = run("icacls", &[&path.to_string_lossy()]);
let mut found = Vec::new();
for (i, line) in text.lines().enumerate() {
let line = line.trim();
if line.is_empty() || line.starts_with("Successfully processed") {
continue;
}
let entry = if i == 0 {
line.strip_prefix(&*path.to_string_lossy())
.unwrap_or(line)
.trim()
} else {
line
};
if entry.contains(":(") {
found.push(entry.to_lowercase());
}
}
found
}
fn only_me(path: &Path, what: &str) {
let e = entries(path);
assert_eq!(e.len(), 1, "{what}: {} has {e:?}", path.display());
assert!(
e[0].starts_with(&format!("{}:", me())) && e[0].contains("(f)") && !e[0].contains("(i)"),
"{what}: {} is {e:?}, want full control for {} and nothing inherited",
path.display(),
me()
);
}
async fn write_one(db: &SqliteBackend) {
db.execute("CREATE TABLE t (n INTEGER)", &[]).await.unwrap();
db.execute("INSERT INTO t VALUES ($1)", &[Value::Int(1)])
.await
.unwrap();
}
#[tokio::test]
async fn the_control_an_ordinary_new_file_is_not_owner_only() {
let dir = scratch("control");
let plain = dir.join("plain.txt");
std::fs::write(&plain, b"x").unwrap();
let e = entries(&plain);
assert!(
e.len() > 1 || e.iter().any(|x| x.contains("(i)")),
"an ordinary file reads as {e:?}"
);
let _ = std::fs::remove_dir_all(&dir);
}
#[tokio::test]
async fn a_new_database_is_owner_only_and_still_works() {
let dir = scratch("open");
let file = dir.join("tenant.sqlite");
let db = SqliteBackend::open(&file).await.unwrap();
write_one(&db).await;
only_me(&file, "open");
drop(db);
let _ = std::fs::remove_dir_all(&dir);
}
#[tokio::test]
async fn a_new_durable_database_and_its_wal_files_are_owner_only() {
let dir = scratch("durable");
let file = dir.join("tenant.sqlite");
let db = SqliteBackend::open_durable(&file).await.unwrap();
write_one(&db).await;
for suffix in ["", "-wal", "-shm"] {
let mut name = file.as_os_str().to_owned();
name.push(suffix);
let p = std::path::PathBuf::from(name);
assert!(p.exists(), "{} should exist while open", p.display());
only_me(&p, "open_durable");
}
drop(db);
let _ = std::fs::remove_dir_all(&dir);
}
#[tokio::test]
async fn an_existing_file_keeps_its_acl() {
let dir = scratch("existing");
let file = dir.join("shared.sqlite");
std::fs::write(&file, b"").unwrap();
let before = entries(&file);
let db = SqliteBackend::open(&file).await.unwrap();
write_one(&db).await;
assert_eq!(
entries(&file),
before,
"an operator's chosen ACL is not changed"
);
drop(db);
let _ = std::fs::remove_dir_all(&dir);
}