Compare commits
1
Commits
841a3a8372
...
73a32cb897
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73a32cb897 |
No files matched your search
@@ -32,3 +32,4 @@ pub mod enroll;
|
||||
pub mod format;
|
||||
pub mod netif;
|
||||
pub mod private;
|
||||
pub mod xdg;
|
||||
@@ -0,0 +1,94 @@
|
||||
//! Where each project keeps the state that must not live in its repo.
|
||||
//!
|
||||
//! Both servers hold the same three things outside their checkout -- the
|
||||
//! config with its token hashes, the CA's private key, and whatever state
|
||||
//! the product itself keeps -- and both resolve the location the same way.
|
||||
//! That is not a coincidence of style: it is forced by the arrangement the
|
||||
//! two projects share.
|
||||
//!
|
||||
//! The repo is a virtiofs mount shared between a machine and a VM at
|
||||
//! *different* absolute paths, so a config inside it would record paths
|
||||
//! that resolve on only one side -- which is exactly how dev-updater came
|
||||
//! to show "not built" for every project on one of them. And the mount is
|
||||
//! writable by the untrusted side, so a CA private key inside it would let
|
||||
//! that side mint a leaf the pinned app trusts, which voids the pinning
|
||||
//! the rest of this crate exists to provide.
|
||||
//!
|
||||
//! Per machine, therefore, and under the XDG directories rather than a
|
||||
//! path of our own choosing, so that a person's existing backup and
|
||||
//! sync rules already cover it.
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// `$XDG_CONFIG_HOME/<product>`, or `~/.config/<product>`.
|
||||
///
|
||||
/// Holds `config.ron` and `certs/`. `product` is a parameter rather than
|
||||
/// a constant because the whole point is that two products keep their
|
||||
/// state apart while resolving it identically.
|
||||
pub fn config_home(product: &str) -> PathBuf {
|
||||
xdg_dir(std::env::var_os("XDG_CONFIG_HOME"), ".config", product)
|
||||
}
|
||||
|
||||
/// `$XDG_DATA_HOME/<product>`, or `~/.local/share/<product>`.
|
||||
///
|
||||
/// Holds whatever the product accumulates rather than is configured with
|
||||
/// -- ai-app's session transcripts and attachments, dev-updater's builds.
|
||||
pub fn data_home(product: &str) -> PathBuf {
|
||||
xdg_dir(std::env::var_os("XDG_DATA_HOME"), ".local/share", product)
|
||||
}
|
||||
|
||||
/// The resolution both of the above use, with the environment handed in.
|
||||
///
|
||||
/// Split out so the rules can be tested without setting process-wide
|
||||
/// environment variables, which two tests running in parallel cannot do
|
||||
/// without racing each other.
|
||||
///
|
||||
/// A relative value is ignored rather than resolved, per the XDG spec: a
|
||||
/// server started from a different working directory would otherwise look
|
||||
/// for its token hashes somewhere new and generate a fresh enrollment,
|
||||
/// silently locking out the phone that was already enrolled.
|
||||
fn xdg_dir(base: Option<OsString>, fallback: &str, product: &str) -> PathBuf {
|
||||
base.map(PathBuf::from)
|
||||
.filter(|path| path.is_absolute())
|
||||
.unwrap_or_else(|| {
|
||||
std::env::home_dir()
|
||||
.unwrap_or_else(|| PathBuf::from("."))
|
||||
.join(fallback)
|
||||
})
|
||||
.join(product)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn an_absolute_setting_is_used_and_namespaced_by_product() {
|
||||
assert_eq!(
|
||||
xdg_dir(Some("/somewhere".into()), ".config", "ai-app"),
|
||||
PathBuf::from("/somewhere/ai-app"),
|
||||
);
|
||||
assert_eq!(
|
||||
xdg_dir(Some("/somewhere".into()), ".config", "dev-updater"),
|
||||
PathBuf::from("/somewhere/dev-updater"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_unset_value_falls_back_under_home() {
|
||||
let dir = xdg_dir(None, ".local/share", "ai-app");
|
||||
assert!(dir.ends_with("ai-app"));
|
||||
assert!(dir.parent().expect("parent").ends_with("share"));
|
||||
}
|
||||
|
||||
/// A relative setting lands on the same path as no setting at all,
|
||||
/// rather than on something that moves with the working directory.
|
||||
#[test]
|
||||
fn a_relative_setting_is_ignored_rather_than_resolved() {
|
||||
assert_eq!(
|
||||
xdg_dir(Some("relative/path".into()), ".config", "ai-app"),
|
||||
xdg_dir(None, ".config", "ai-app"),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user