restructure files

This commit is contained in:
Vincent Breitmoser 2019-04-26 18:33:26 +02:00
parent 5900f1a8f4
commit 385efb37b3
20 changed files with 231 additions and 89 deletions

View File

@ -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"

View File

@ -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<P: Into<PathBuf>>(base: P) -> Result<Self> {
pub fn new_from_base(base_dir: impl Into<PathBuf>) -> Result<Self> {
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<PathBuf>,
state_dir: impl Into<PathBuf>,
tmp_dir: impl Into<PathBuf>,
) -> Result<Self> {
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::<String>();
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<Box<[u8]>> {
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<String>,
) -> 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));
}

View File

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

88
dist/assets/img/gnupg.svg vendored Normal file
View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://web.resource.org/cc/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.45"
width="70"
height="92"
version="1.0"
sodipodi:docbase="D:\Desktop"
sodipodi:docname="GnuPG-Logo.png"
sodipodi:modified="true">
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs5" />
<sodipodi:namedview
inkscape:window-height="975"
inkscape:window-width="1280"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
width="70px"
height="92px"
inkscape:zoom="4"
inkscape:cx="37.678772"
inkscape:cy="50.578436"
inkscape:window-x="-4"
inkscape:window-y="-4"
inkscape:current-layer="svg2" />
<rect
style="opacity:1;fill:#0093dd;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect3132"
width="70"
height="47.099998"
x="0"
y="44.900002" />
<rect
style="opacity:1;fill:#0093dd;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4103"
width="15.009007"
height="19.485378"
x="4.7396865"
y="25.468845" />
<rect
style="opacity:1;fill:#0093dd;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4105"
width="15.18455"
height="19.239996"
x="50.117794"
y="25.694933" />
<path
style="opacity:1;fill:#0093dd;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 35.625,0 C 13.155955,0.16164779 4.75,20.998357 4.75,25.5625 C 10.896546,25.562499 17.561658,25.90625 18.96875,25.90625 C 19.298759,25.90625 19.367598,25.906322 19.6875,25.90625 C 21.365862,19.035999 27.552617,13.9375 34.9375,13.9375 C 42.289526,13.9375 48.445364,18.988532 50.15625,25.8125 C 50.543323,25.812313 50.720292,25.8125 51.125,25.8125 C 63.764162,25.8125 65.25,25.90625 65.25,25.90625 C 65.25,25.90625 60.201152,-0.17554394 35.625,0 z "
id="path6045" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1"
d="M 10.269321,25.20553 C 21.691029,1.9172856 37.700972,5.1573508 46.958242,6.5921309 C 46.958242,6.5921309 34.785164,0.76242296 23.084029,6.5100997 C 11.608567,12.146925 10.269321,25.20553 10.269321,25.20553 z "
id="path7026"
sodipodi:nodetypes="ccsc" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1"
d="M -0.25,68.25 C -0.25,68.25 7.5390098,59.548683 13.8125,57.25 C 22,54.25 32,55.1875 48.5625,51.875 C 54.425075,50.702485 56.25,49.25 65.1875,44.625 C 65.942492,44.234305 70.5625,44.3125 70.5625,44.3125 L 70.1875,50.5625 C 60,61.25 36.75,67.21875 34.8125,67.125 C 54.479984,69.763423 64.97809,59.568082 69.25,58.9375 C 59.3125,80.0625 24.0625,79 24.0625,79 C 44.5,84.5625 61.125,76.25 61.125,76.25 C 51.8125,91.65625 17.4375,89.5625 17.4375,89.5625 C 13.84375,89.875 10.9375,92.1875 10.9375,92.1875 L -0.125,92.5 L -0.25,68.25 z "
id="path7997"
sodipodi:nodetypes="cssscccccccccc" />
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -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)
}

View File

@ -56,7 +56,7 @@ fn main() {
}
fn do_import(base: PathBuf, keyrings: Vec<PathBuf>) -> 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(

View File

@ -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<Polymorphic>,
#[get("/assets/<file..>")]
fn files(file: PathBuf, state: rocket::State<HagridState>) -> Option<NamedFile> {
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<rocket::Rocket> {
use std::convert::TryFrom;
let routes = routes![
// infra
root,
@ -362,25 +363,62 @@ fn rocket_factory(rocket: rocket::Rocket) -> Result<rocket::Rocket> {
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<Polymorphic> {
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<HagridState> {
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<tokens::Service> {
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> {
// 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<rocket::Rocket> {
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")