From 385efb37b3b76692aa13dc06745eee22dd507b6e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 26 Apr 2019 18:33:26 +0200 Subject: [PATCH] restructure files --- Rocket.toml.dist | 5 +- database/src/fs.rs | 104 ++++++++++------ .../assets/fonts/fa-regular-400.eot | Bin .../assets/fonts/fa-regular-400.svg | 0 .../assets/fonts/fa-regular-400.ttf | Bin .../assets/fonts/fa-regular-400.woff | Bin .../assets/fonts/fa-regular-400.woff2 | Bin .../assets/fonts/fontawesome.min.css | 0 .../assets/fonts/roboto-v18-latin-300.eot | Bin .../assets/fonts/roboto-v18-latin-300.svg | 0 .../assets/fonts/roboto-v18-latin-300.ttf | Bin .../assets/fonts/roboto-v18-latin-300.woff | Bin .../assets/fonts/roboto-v18-latin-300.woff2 | Bin dist/assets/img/gnupg.svg | 88 +++++++++++++ dist/{public => }/assets/img/grey.png | Bin dist/{public => }/assets/site.css | 0 dist/{public => }/assets/site.js | 0 src/delete.rs | 2 +- src/import.rs | 4 +- src/web/mod.rs | 117 +++++++++++------- 20 files changed, 231 insertions(+), 89 deletions(-) rename dist/{public => }/assets/fonts/fa-regular-400.eot (100%) rename dist/{public => }/assets/fonts/fa-regular-400.svg (100%) rename dist/{public => }/assets/fonts/fa-regular-400.ttf (100%) rename dist/{public => }/assets/fonts/fa-regular-400.woff (100%) rename dist/{public => }/assets/fonts/fa-regular-400.woff2 (100%) rename dist/{public => }/assets/fonts/fontawesome.min.css (100%) rename dist/{public => }/assets/fonts/roboto-v18-latin-300.eot (100%) rename dist/{public => }/assets/fonts/roboto-v18-latin-300.svg (100%) rename dist/{public => }/assets/fonts/roboto-v18-latin-300.ttf (100%) rename dist/{public => }/assets/fonts/roboto-v18-latin-300.woff (100%) rename dist/{public => }/assets/fonts/roboto-v18-latin-300.woff2 (100%) create mode 100644 dist/assets/img/gnupg.svg rename dist/{public => }/assets/img/grey.png (100%) rename dist/{public => }/assets/site.css (100%) rename dist/{public => }/assets/site.js (100%) diff --git a/Rocket.toml.dist b/Rocket.toml.dist index 651eb6e..68da577 100644 --- a/Rocket.toml.dist +++ b/Rocket.toml.dist @@ -2,7 +2,10 @@ address = "0.0.0.0" port = 8080 template_dir = "dist/templates" -state_dir = "dist" +assets_dir = "dist/assets" +keys_dir = "dist/keys" +state_dir = "dist/hagrid_state" +tmp_dir = "dist/tmp" [development] base-URI = "http://localhost:8080" diff --git a/database/src/fs.rs b/database/src/fs.rs index c188ffb..6e8d7b5 100644 --- a/database/src/fs.rs +++ b/database/src/fs.rs @@ -19,10 +19,12 @@ use Result; pub struct Filesystem { update_lock: FlockMutex, - base: PathBuf, - base_by_keyid: PathBuf, - base_by_fingerprint: PathBuf, - base_by_email: PathBuf, + state_dir: PathBuf, + tmp_dir: PathBuf, + + keys_dir_by_keyid: PathBuf, + keys_dir_by_fingerprint: PathBuf, + keys_dir_by_email: PathBuf, } /// Returns the given path, ensuring that the parent directory exists. @@ -37,68 +39,89 @@ fn ensure_parent(path: &Path) -> Result<&Path> { impl Filesystem { - pub fn new>(base: P) -> Result { + pub fn new_from_base(base_dir: impl Into) -> Result { + let base_dir: PathBuf = base_dir.into(); + + let keys_dir = base_dir.join("keys"); + let state_dir = base_dir.join("hagrid_state"); + let tmp_dir = base_dir.join("tmp"); + + Self::new(keys_dir, state_dir, tmp_dir) + } + + pub fn new( + keys_dir: impl Into, + state_dir: impl Into, + tmp_dir: impl Into, + ) -> Result { use std::fs; - let base: PathBuf = base.into(); + let state_dir = state_dir.into(); - if fs::create_dir(&base).is_err() { - let meta = fs::metadata(&base); + if fs::create_dir(&state_dir).is_err() { + let meta = fs::metadata(&state_dir); match meta { Ok(meta) => { if !meta.file_type().is_dir() { return Err(failure::format_err!( "'{}' exists already and is not a directory", - base.display())); + state_dir.display())); } if meta.permissions().readonly() { return Err(failure::format_err!( "Cannot write '{}'", - base.display())); + state_dir.display())); } } Err(e) => { return Err(failure::format_err!( "Cannot read '{}': {}", - base.display(), e)); + state_dir.display(), e)); } } } - // create directories - create_dir_all(base.join("verification_tokens"))?; - create_dir_all(base.join("scratch_pad"))?; + let tmp_dir = tmp_dir.into(); + create_dir_all(&tmp_dir)?; - let base_by_keyid = base.join("public").join("by-keyid"); - let base_by_fingerprint = base.join("public").join("by-fpr"); - let base_by_email = base.join("public").join("by-email"); - create_dir_all(&base_by_keyid)?; - create_dir_all(&base_by_fingerprint)?; - create_dir_all(&base_by_email)?; + let token_dir = state_dir.join("verification_tokens"); + create_dir_all(token_dir)?; - info!("Opened base dir '{}'", base.display()); + let keys_dir: PathBuf = keys_dir.into(); + let keys_dir_by_keyid = keys_dir.join("by-keyid"); + let keys_dir_by_fingerprint = keys_dir.join("by-fpr"); + let keys_dir_by_email = keys_dir.join("by-email"); + create_dir_all(&keys_dir_by_keyid)?; + create_dir_all(&keys_dir_by_fingerprint)?; + create_dir_all(&keys_dir_by_email)?; + + info!("Opened filesystem database."); + info!("keys_dir: '{}'", keys_dir.display()); + info!("state_dir: '{}'", state_dir.display()); + info!("tmp_dir: '{}'", tmp_dir.display()); Ok(Filesystem { - update_lock: FlockMutex::new(&base)?, - base: base, - base_by_keyid: base_by_keyid, - base_by_fingerprint: base_by_fingerprint, - base_by_email: base_by_email, + update_lock: FlockMutex::new(&state_dir)?, + state_dir: state_dir, + tmp_dir: tmp_dir, + keys_dir_by_keyid: keys_dir_by_keyid, + keys_dir_by_fingerprint: keys_dir_by_fingerprint, + keys_dir_by_email: keys_dir_by_email, }) } /// Returns the path to the given KeyID. fn keyid_to_path(&self, keyid: &KeyID) -> PathBuf { let hex = keyid.to_string(); - self.base_by_keyid.join(&hex[..2]).join(&hex[2..]) + self.keys_dir_by_keyid.join(&hex[..2]).join(&hex[2..]) } /// Returns the path to the given Fingerprint. fn fingerprint_to_path(&self, fingerprint: &Fingerprint) -> PathBuf { let hex = fingerprint.to_string(); - self.base_by_fingerprint.join(&hex[..2]).join(&hex[2..]) + self.keys_dir_by_fingerprint.join(&hex[..2]).join(&hex[2..]) } /// Returns the path to the given Email. @@ -107,9 +130,9 @@ impl Filesystem { url::form_urlencoded::byte_serialize(email.as_str().as_bytes()) .collect::(); if email.len() > 2 { - self.base_by_email.join(&email[..2]).join(&email[2..]) + self.keys_dir_by_email.join(&email[..2]).join(&email[2..]) } else { - self.base_by_email.join(email) + self.keys_dir_by_email.join(email) } } @@ -178,7 +201,7 @@ impl Filesystem { Email::from_str(&decoded).ok() } - fn new_token<'a>(&self, base: &'a str) -> Result<(File, String)> { + fn new_token<'a>(&self, token_type: &'a str) -> Result<(File, String)> { use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; @@ -186,16 +209,16 @@ impl Filesystem { // samples from [a-zA-Z0-9] // 43 chars ~ 256 bit let name: String = rng.sample_iter(&Alphanumeric).take(43).collect(); - let dir = self.base.join(base); + let dir = self.state_dir.join(token_type); let fd = File::create(dir.join(&name))?; Ok((fd, name)) } fn pop_token<'a>( - &self, base: &'a str, token: &'a str, + &self, token_type: &'a str, token: &'a str, ) -> Result> { - let path = self.base.join(base).join(token); + let path = self.state_dir.join(token_type).join(token); let buf = { let mut fd = File::open(&path)?; let mut buf = Vec::default(); @@ -221,7 +244,7 @@ impl Filesystem { let mut tpks = HashMap::new(); // Check Fingerprints. - for entry in fs::read_dir(&self.base_by_fingerprint)? { + for entry in fs::read_dir(&self.keys_dir_by_fingerprint)? { let prefix = entry?; let prefix_path = prefix.path(); if ! prefix_path.is_dir() { @@ -293,7 +316,7 @@ impl Filesystem { } // Check KeyIDs. - for entry in fs::read_dir(&self.base_by_keyid)? { + for entry in fs::read_dir(&self.keys_dir_by_keyid)? { let prefix = entry?; let prefix_path = prefix.path(); if ! prefix_path.is_dir() { @@ -344,7 +367,7 @@ impl Filesystem { } // Check Emails. - for entry in fs::read_dir(&self.base_by_email)? { + for entry in fs::read_dir(&self.keys_dir_by_email)? { let prefix = entry?; let prefix_path = prefix.path(); if ! prefix_path.is_dir() { @@ -431,14 +454,13 @@ impl Database for Filesystem { &self, fpr: &Fingerprint, new: Option, ) -> Result<()> { let target = self.fingerprint_to_path(fpr); - let dir = self.base.join("scratch_pad"); match new { Some(new) => { let mut tmp = tempfile::Builder::new() .prefix("key") .rand_bytes(16) - .tempfile_in(dir)?; + .tempfile_in(&self.tmp_dir)?; tmp.write_all(new.as_bytes()).unwrap(); let _ = tmp.persist(ensure_parent(&target)?)?; @@ -505,7 +527,7 @@ impl Database for Filesystem { }; if path.exists() { - Some(diff_paths(&path, &self.base).expect("related paths")) + Some(diff_paths(&path, &self.state_dir).expect("related paths")) } else { None } @@ -786,7 +808,7 @@ mod tests { // same two nibble prefix. assert_eq!( db.path_to_fingerprint_base( - &db.base_by_fingerprint.join("CB"), + &db.keys_dir_by_fingerprint.join("CB"), &PathBuf::from("CD8F030588653EEDD7E2659B7DD433F254904A")), Some(fp)); } diff --git a/dist/public/assets/fonts/fa-regular-400.eot b/dist/assets/fonts/fa-regular-400.eot similarity index 100% rename from dist/public/assets/fonts/fa-regular-400.eot rename to dist/assets/fonts/fa-regular-400.eot diff --git a/dist/public/assets/fonts/fa-regular-400.svg b/dist/assets/fonts/fa-regular-400.svg similarity index 100% rename from dist/public/assets/fonts/fa-regular-400.svg rename to dist/assets/fonts/fa-regular-400.svg diff --git a/dist/public/assets/fonts/fa-regular-400.ttf b/dist/assets/fonts/fa-regular-400.ttf similarity index 100% rename from dist/public/assets/fonts/fa-regular-400.ttf rename to dist/assets/fonts/fa-regular-400.ttf diff --git a/dist/public/assets/fonts/fa-regular-400.woff b/dist/assets/fonts/fa-regular-400.woff similarity index 100% rename from dist/public/assets/fonts/fa-regular-400.woff rename to dist/assets/fonts/fa-regular-400.woff diff --git a/dist/public/assets/fonts/fa-regular-400.woff2 b/dist/assets/fonts/fa-regular-400.woff2 similarity index 100% rename from dist/public/assets/fonts/fa-regular-400.woff2 rename to dist/assets/fonts/fa-regular-400.woff2 diff --git a/dist/public/assets/fonts/fontawesome.min.css b/dist/assets/fonts/fontawesome.min.css similarity index 100% rename from dist/public/assets/fonts/fontawesome.min.css rename to dist/assets/fonts/fontawesome.min.css diff --git a/dist/public/assets/fonts/roboto-v18-latin-300.eot b/dist/assets/fonts/roboto-v18-latin-300.eot similarity index 100% rename from dist/public/assets/fonts/roboto-v18-latin-300.eot rename to dist/assets/fonts/roboto-v18-latin-300.eot diff --git a/dist/public/assets/fonts/roboto-v18-latin-300.svg b/dist/assets/fonts/roboto-v18-latin-300.svg similarity index 100% rename from dist/public/assets/fonts/roboto-v18-latin-300.svg rename to dist/assets/fonts/roboto-v18-latin-300.svg diff --git a/dist/public/assets/fonts/roboto-v18-latin-300.ttf b/dist/assets/fonts/roboto-v18-latin-300.ttf similarity index 100% rename from dist/public/assets/fonts/roboto-v18-latin-300.ttf rename to dist/assets/fonts/roboto-v18-latin-300.ttf diff --git a/dist/public/assets/fonts/roboto-v18-latin-300.woff b/dist/assets/fonts/roboto-v18-latin-300.woff similarity index 100% rename from dist/public/assets/fonts/roboto-v18-latin-300.woff rename to dist/assets/fonts/roboto-v18-latin-300.woff diff --git a/dist/public/assets/fonts/roboto-v18-latin-300.woff2 b/dist/assets/fonts/roboto-v18-latin-300.woff2 similarity index 100% rename from dist/public/assets/fonts/roboto-v18-latin-300.woff2 rename to dist/assets/fonts/roboto-v18-latin-300.woff2 diff --git a/dist/assets/img/gnupg.svg b/dist/assets/img/gnupg.svg new file mode 100644 index 0000000..7671d73 --- /dev/null +++ b/dist/assets/img/gnupg.svg @@ -0,0 +1,88 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/dist/public/assets/img/grey.png b/dist/assets/img/grey.png similarity index 100% rename from dist/public/assets/img/grey.png rename to dist/assets/img/grey.png diff --git a/dist/public/assets/site.css b/dist/assets/site.css similarity index 100% rename from dist/public/assets/site.css rename to dist/assets/site.css diff --git a/dist/public/assets/site.js b/dist/assets/site.js similarity index 100% rename from dist/public/assets/site.js rename to dist/assets/site.js diff --git a/src/delete.rs b/src/delete.rs index 7873ec4..198c97a 100644 --- a/src/delete.rs +++ b/src/delete.rs @@ -51,7 +51,7 @@ fn main() { fn real_main() -> Result<()> { let opt = Opt::from_args(); - let db = Filesystem::new(opt.base.canonicalize()?)?; + let db = Filesystem::new_from_base(opt.base.canonicalize()?)?; delete(&db, &opt.query.parse()?, opt.all_bindings, opt.all) } diff --git a/src/import.rs b/src/import.rs index 04555a4..ad9a6cc 100644 --- a/src/import.rs +++ b/src/import.rs @@ -56,7 +56,7 @@ fn main() { } fn do_import(base: PathBuf, keyrings: Vec) -> Result<()> { - let db = Filesystem::new(base)?; + let db = Filesystem::new_from_base(base)?; // For each input file, create a parser. for input in keyrings.iter() { @@ -110,7 +110,7 @@ mod import_tests { fn import() { let root = tempdir().unwrap(); - let db = Filesystem::new(root.path().to_path_buf()).unwrap(); + let db = Filesystem::new_from_base(root.path().to_path_buf()).unwrap(); // Generate a key and import it. let (tpk, _) = openpgp::tpk::TPKBuilder::autocrypt( diff --git a/src/web/mod.rs b/src/web/mod.rs index b5ddde2..a2c7f03 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -1,6 +1,7 @@ use rocket; use rocket::http::Header; use rocket::response::NamedFile; +use rocket::config::Config; use rocket_contrib::templates::Template; use serde::Serialize; @@ -65,6 +66,7 @@ impl MyResponse { }) } + // XXX needs fixing for keys_dir! pub fn x_accel_redirect(path: PathBuf, fp: &Fingerprint) -> Self { use rocket::http::hyper::header::{ContentDisposition, DispositionType, DispositionParam, Charset}; @@ -170,13 +172,14 @@ mod templates { } pub struct HagridState { - /// The base directory. + /// State directory, used internally by hagrid state_dir: PathBuf, - /// The public directory. - /// - /// This is what nginx serves. - public_dir: PathBuf, + /// Assets directory, mounted to /assets, served by hagrid or nginx + assets_dir: PathBuf, + + /// The keys directory, where keys are located, served by hagrid or nginx + keys_dir: PathBuf, /// XXX base_uri: String, @@ -307,7 +310,7 @@ fn publish_verify(db: rocket::State, #[get("/assets/")] fn files(file: PathBuf, state: rocket::State) -> Option { - NamedFile::open(state.public_dir.join("assets").join(file)).ok() + NamedFile::open(state.assets_dir.join(file)).ok() } #[get("/")] @@ -335,8 +338,6 @@ pub fn serve() -> Result<()> { } fn rocket_factory(rocket: rocket::Rocket) -> Result { - use std::convert::TryFrom; - let routes = routes![ // infra root, @@ -362,25 +363,62 @@ fn rocket_factory(rocket: rocket::Rocket) -> Result { manage::vks_manage_unpublish, ]; + let db_service = configure_db_service(rocket.config())?; + let hagrid_state = configure_hagrid_state(rocket.config())?; + let token_service = configure_token_service(rocket.config())?; + let mail_service = configure_mail_service(rocket.config())?; + + Ok(rocket + .attach(Template::fairing()) + .manage(hagrid_state) + .manage(token_service) + .manage(mail_service) + .manage(db_service) + .mount("/", routes) + ) +} + +fn configure_db_service(config: &Config) -> Result { use database::{Filesystem, Polymorphic}; - let db = Polymorphic::Filesystem( - Filesystem::new(&PathBuf::from(rocket.config().get_str("state_dir")?))? - ); + + let keys_dir: PathBuf = config.get_str("keys_dir")?.into(); + let state_dir: PathBuf = config.get_str("state_dir")?.into(); + let tmp_dir: PathBuf = config.get_str("tmp_dir")?.into(); + + let fs_db = Filesystem::new(keys_dir, state_dir, tmp_dir)?; + Ok(Polymorphic::Filesystem(fs_db)) +} + +fn configure_hagrid_state(config: &Config) -> Result { + let state_dir: PathBuf = config.get_str("state_dir")?.into(); + let assets_dir: PathBuf = config.get_str("assets_dir")?.into(); + let keys_dir: PathBuf = config.get_str("keys_dir")?.into(); // State - let state_dir: PathBuf = rocket.config().get_str("state_dir")?.into(); - let public_dir = state_dir.join("public"); - let base_uri = rocket.config().get_str("base-URI")?.to_string(); - let state = HagridState { - state_dir: state_dir, - public_dir: public_dir, + let base_uri = config.get_str("base-URI")?.to_string(); + Ok(HagridState { + state_dir, + assets_dir, + keys_dir, base_uri: base_uri.clone(), - x_accel_redirect: rocket.config().get_bool("x-accel-redirect")?, - }; + x_accel_redirect: config.get_bool("x-accel-redirect")?, + }) +} +fn configure_token_service(config: &Config) -> Result { + use std::convert::TryFrom; + + let secret = config.get_str("token_secret")?.to_string(); + let validity = config.get_int("token_validity")?; + let validity = u64::try_from(validity)?; + Ok(tokens::Service::init(&secret, validity)) +} + +fn configure_mail_service(config: &Config) -> Result { // Mail service - let template_dir: PathBuf = rocket.config().get_str("template_dir")?.into(); - let from = rocket.config().get_str("from")?.to_string(); + let template_dir: PathBuf = config.get_str("template_dir")?.into(); + let base_uri = config.get_str("base-URI")?.to_string(); + let from = config.get_str("from")?.to_string(); let verify_html = template_dir.join("email/publish-html.hbs"); let verify_txt = template_dir.join("email/publish-txt.hbs"); let manage_html = template_dir.join("email/manage-html.hbs"); @@ -392,26 +430,14 @@ fn rocket_factory(rocket: rocket::Rocket) -> Result { handlebars.register_template_file("manage-html", manage_html)?; handlebars.register_template_file("manage-txt", manage_txt)?; - let filemail_into = rocket.config().get_str("filemail_into") + let filemail_into = config.get_str("filemail_into") .ok().map(|p| PathBuf::from(p)); - let mail_service = if let Some(path) = filemail_into { - mail::Service::filemail(from, base_uri, handlebars, path)? + + if let Some(path) = filemail_into { + mail::Service::filemail(from, base_uri, handlebars, path) } else { - mail::Service::sendmail(from, base_uri, handlebars)? - }; - - let secret = rocket.config().get_str("token_secret")?.to_string(); - let validity = rocket.config().get_int("token_validity")?; - let validity = u64::try_from(validity)?; - let token_service = tokens::Service::init(&secret, validity); - - Ok(rocket - .attach(Template::fairing()) - .manage(state) - .manage(mail_service) - .manage(token_service) - .mount("/", routes) - .manage(db)) + mail::Service::sendmail(from, base_uri, handlebars) + } } #[cfg(test)] @@ -449,16 +475,19 @@ pub mod tests { let filemail = root.path().join("filemail"); ::std::fs::create_dir_all(&filemail)?; + let base_dir: PathBuf = root.path().into(); + let config = Config::build(Environment::Staging) .root(root.path().to_path_buf()) .extra("template_dir", ::std::env::current_dir().unwrap().join("dist/templates") .to_str().unwrap()) - .extra( - "state_dir", - root.path().to_str() - .ok_or(failure::err_msg("Static path invalid"))?, - ) + .extra("assets_dir", + ::std::env::current_dir().unwrap().join("dist/assets") + .to_str().unwrap()) + .extra("keys_dir", base_dir.join("keys").to_str().unwrap()) + .extra("tmp_dir", base_dir.join("tmp").to_str().unwrap()) + .extra("state_dir", base_dir.join("state").to_str().unwrap()) .extra("base-URI", BASE_URI) .extra("from", "from") .extra("token_secret", "hagrid")