use std::path::PathBuf;
use crate::error::{Result, ShoreError};
pub(crate) fn keys_dir() -> Result<PathBuf> {
let keys = crate::shore_home::shore_home_root()?.join("keys");
create_private_dir(&keys)?;
Ok(keys)
}
fn create_private_dir(dir: &std::path::Path) -> Result<()> {
std::fs::create_dir_all(dir).map_err(|error| {
ShoreError::Message(format!("create key home {}: {error}", dir.display()))
})?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt as _;
std::fs::set_permissions(dir, std::fs::Permissions::from_mode(0o700)).map_err(|error| {
ShoreError::Message(format!("set 0700 on {}: {error}", dir.display()))
})?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keys_dir_under_override_creates_keys_subtree_deterministically() {
let tmp = tempfile::tempdir().unwrap();
unsafe {
std::env::set_var(crate::shore_home::SHORE_HOME_ENV, tmp.path());
}
let first = keys_dir().unwrap();
let second = keys_dir().unwrap();
unsafe {
std::env::remove_var(crate::shore_home::SHORE_HOME_ENV);
}
assert_eq!(first, second, "resolution is deterministic under override");
assert_eq!(first, tmp.path().join("keys"));
assert!(first.is_dir(), "the keys/ subtree is created");
}
#[cfg(unix)]
#[test]
fn created_keys_directory_is_0700_on_unix() {
use std::os::unix::fs::PermissionsExt as _;
let tmp = tempfile::tempdir().unwrap();
unsafe {
std::env::set_var(crate::shore_home::SHORE_HOME_ENV, tmp.path());
}
let dir = keys_dir().unwrap();
unsafe {
std::env::remove_var(crate::shore_home::SHORE_HOME_ENV);
}
let mode = std::fs::metadata(&dir).unwrap().permissions().mode();
assert_eq!(mode & 0o777, 0o700, "keystore dir must be private");
}
}