mirror of
https://gitlab.com/hagrid-keyserver/hagrid.git
synced 2023-02-13 20:55:02 -05:00
extract stateful token functionality from database
This commit is contained in:
parent
385efb37b3
commit
960c0d89f7
9 changed files with 202 additions and 206 deletions
|
@ -1,17 +1,15 @@
|
||||||
use std::convert::{TryInto, TryFrom};
|
use std::convert::{TryInto, TryFrom};
|
||||||
use std::fs::{create_dir_all, read_link, remove_file, rename, File};
|
use std::fs::{create_dir_all, read_link, remove_file, rename};
|
||||||
use std::io::{Read, Write};
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str;
|
|
||||||
|
|
||||||
use serde_json;
|
|
||||||
use tempfile;
|
use tempfile;
|
||||||
use url;
|
use url;
|
||||||
use pathdiff::diff_paths;
|
use pathdiff::diff_paths;
|
||||||
|
|
||||||
//use sequoia_openpgp::armor::{Writer, Kind};
|
//use sequoia_openpgp::armor::{Writer, Kind};
|
||||||
|
|
||||||
use {Database, Verify, Query};
|
use {Database, Query};
|
||||||
use types::{Email, Fingerprint, KeyID};
|
use types::{Email, Fingerprint, KeyID};
|
||||||
use sync::{MutexGuard, FlockMutex};
|
use sync::{MutexGuard, FlockMutex};
|
||||||
use Result;
|
use Result;
|
||||||
|
@ -19,9 +17,9 @@ use Result;
|
||||||
pub struct Filesystem {
|
pub struct Filesystem {
|
||||||
update_lock: FlockMutex,
|
update_lock: FlockMutex,
|
||||||
|
|
||||||
state_dir: PathBuf,
|
|
||||||
tmp_dir: PathBuf,
|
tmp_dir: PathBuf,
|
||||||
|
|
||||||
|
keys_dir: PathBuf,
|
||||||
keys_dir_by_keyid: PathBuf,
|
keys_dir_by_keyid: PathBuf,
|
||||||
keys_dir_by_fingerprint: PathBuf,
|
keys_dir_by_fingerprint: PathBuf,
|
||||||
keys_dir_by_email: PathBuf,
|
keys_dir_by_email: PathBuf,
|
||||||
|
@ -43,22 +41,19 @@ impl Filesystem {
|
||||||
let base_dir: PathBuf = base_dir.into();
|
let base_dir: PathBuf = base_dir.into();
|
||||||
|
|
||||||
let keys_dir = base_dir.join("keys");
|
let keys_dir = base_dir.join("keys");
|
||||||
let state_dir = base_dir.join("hagrid_state");
|
|
||||||
let tmp_dir = base_dir.join("tmp");
|
let tmp_dir = base_dir.join("tmp");
|
||||||
|
|
||||||
Self::new(keys_dir, state_dir, tmp_dir)
|
Self::new(keys_dir, tmp_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
keys_dir: impl Into<PathBuf>,
|
keys_dir: impl Into<PathBuf>,
|
||||||
state_dir: impl Into<PathBuf>,
|
|
||||||
tmp_dir: impl Into<PathBuf>,
|
tmp_dir: impl Into<PathBuf>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
let state_dir = state_dir.into();
|
/*
|
||||||
|
use std::fs;
|
||||||
if fs::create_dir(&state_dir).is_err() {
|
if fs::create_dir(&state_dir).is_err() {
|
||||||
let meta = fs::metadata(&state_dir);
|
let meta = fs::metadata(&state_dir);
|
||||||
|
|
||||||
match meta {
|
match meta {
|
||||||
|
@ -82,14 +77,11 @@ impl Filesystem {
|
||||||
state_dir.display(), e));
|
state_dir.display(), e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
let tmp_dir = tmp_dir.into();
|
let tmp_dir = tmp_dir.into();
|
||||||
create_dir_all(&tmp_dir)?;
|
create_dir_all(&tmp_dir)?;
|
||||||
|
|
||||||
let token_dir = state_dir.join("verification_tokens");
|
|
||||||
create_dir_all(token_dir)?;
|
|
||||||
|
|
||||||
let keys_dir: PathBuf = keys_dir.into();
|
let keys_dir: PathBuf = keys_dir.into();
|
||||||
let keys_dir_by_keyid = keys_dir.join("by-keyid");
|
let keys_dir_by_keyid = keys_dir.join("by-keyid");
|
||||||
let keys_dir_by_fingerprint = keys_dir.join("by-fpr");
|
let keys_dir_by_fingerprint = keys_dir.join("by-fpr");
|
||||||
|
@ -100,11 +92,10 @@ impl Filesystem {
|
||||||
|
|
||||||
info!("Opened filesystem database.");
|
info!("Opened filesystem database.");
|
||||||
info!("keys_dir: '{}'", keys_dir.display());
|
info!("keys_dir: '{}'", keys_dir.display());
|
||||||
info!("state_dir: '{}'", state_dir.display());
|
|
||||||
info!("tmp_dir: '{}'", tmp_dir.display());
|
info!("tmp_dir: '{}'", tmp_dir.display());
|
||||||
Ok(Filesystem {
|
Ok(Filesystem {
|
||||||
update_lock: FlockMutex::new(&state_dir)?,
|
update_lock: FlockMutex::new(&keys_dir)?,
|
||||||
state_dir: state_dir,
|
keys_dir: keys_dir,
|
||||||
tmp_dir: tmp_dir,
|
tmp_dir: tmp_dir,
|
||||||
keys_dir_by_keyid: keys_dir_by_keyid,
|
keys_dir_by_keyid: keys_dir_by_keyid,
|
||||||
keys_dir_by_fingerprint: keys_dir_by_fingerprint,
|
keys_dir_by_fingerprint: keys_dir_by_fingerprint,
|
||||||
|
@ -201,36 +192,6 @@ impl Filesystem {
|
||||||
Email::from_str(&decoded).ok()
|
Email::from_str(&decoded).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_token<'a>(&self, token_type: &'a str) -> Result<(File, String)> {
|
|
||||||
use rand::distributions::Alphanumeric;
|
|
||||||
use rand::{thread_rng, Rng};
|
|
||||||
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
// samples from [a-zA-Z0-9]
|
|
||||||
// 43 chars ~ 256 bit
|
|
||||||
let name: String = rng.sample_iter(&Alphanumeric).take(43).collect();
|
|
||||||
let dir = self.state_dir.join(token_type);
|
|
||||||
let fd = File::create(dir.join(&name))?;
|
|
||||||
|
|
||||||
Ok((fd, name))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_token<'a>(
|
|
||||||
&self, token_type: &'a str, token: &'a str,
|
|
||||||
) -> Result<Box<[u8]>> {
|
|
||||||
let path = self.state_dir.join(token_type).join(token);
|
|
||||||
let buf = {
|
|
||||||
let mut fd = File::open(&path)?;
|
|
||||||
let mut buf = Vec::default();
|
|
||||||
|
|
||||||
fd.read_to_end(&mut buf)?;
|
|
||||||
buf.into_boxed_slice()
|
|
||||||
};
|
|
||||||
|
|
||||||
remove_file(path)?;
|
|
||||||
Ok(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks the database for consistency.
|
/// Checks the database for consistency.
|
||||||
///
|
///
|
||||||
/// Note that this operation may take a long time, and is
|
/// Note that this operation may take a long time, and is
|
||||||
|
@ -443,13 +404,6 @@ impl Database for Filesystem {
|
||||||
self.update_lock.lock().into()
|
self.update_lock.lock().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_verify_token(&self, payload: Verify) -> Result<String> {
|
|
||||||
let (mut fd, name) = self.new_token("verification_tokens")?;
|
|
||||||
fd.write_all(serde_json::to_string(&payload)?.as_bytes())?;
|
|
||||||
|
|
||||||
Ok(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(
|
fn update(
|
||||||
&self, fpr: &Fingerprint, new: Option<String>,
|
&self, fpr: &Fingerprint, new: Option<String>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
@ -527,8 +481,11 @@ impl Database for Filesystem {
|
||||||
};
|
};
|
||||||
|
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
Some(diff_paths(&path, &self.state_dir).expect("related paths"))
|
let x = diff_paths(&path, &self.keys_dir).expect("related paths");
|
||||||
|
println!("YEAP: {:?}", &x);
|
||||||
|
Some(x)
|
||||||
} else {
|
} else {
|
||||||
|
println!("NOPE");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -631,16 +588,6 @@ impl Database for Filesystem {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_verify_token(&self, token: &str) -> Option<Verify> {
|
|
||||||
self.pop_token("verification_tokens", token)
|
|
||||||
.ok()
|
|
||||||
.and_then(|raw| str::from_utf8(&raw).ok().map(|s| s.to_string()))
|
|
||||||
.and_then(|s| {
|
|
||||||
let s = serde_json::from_str(&s);
|
|
||||||
s.ok()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX: slow
|
// XXX: slow
|
||||||
fn by_fpr(&self, fpr: &Fingerprint) -> Option<String> {
|
fn by_fpr(&self, fpr: &Fingerprint) -> Option<String> {
|
||||||
let path = self.fingerprint_to_path(fpr);
|
let path = self.fingerprint_to_path(fpr);
|
||||||
|
@ -677,13 +624,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn init() {
|
fn init() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let _ = Filesystem::new(tmpdir.path()).unwrap();
|
let _ = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new() {
|
fn new() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let db = Filesystem::new(tmpdir.path()).unwrap();
|
let db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
let k1 = TPKBuilder::default().add_userid("a@invalid.example.org")
|
let k1 = TPKBuilder::default().add_userid("a@invalid.example.org")
|
||||||
.generate().unwrap().0;
|
.generate().unwrap().0;
|
||||||
let k2 = TPKBuilder::default().add_userid("b@invalid.example.org")
|
let k2 = TPKBuilder::default().add_userid("b@invalid.example.org")
|
||||||
|
@ -702,7 +649,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn uid_verification() {
|
fn uid_verification() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
|
|
||||||
test::test_uid_verification(&mut db);
|
test::test_uid_verification(&mut db);
|
||||||
}
|
}
|
||||||
|
@ -710,7 +657,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn uid_deletion() {
|
fn uid_deletion() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
|
|
||||||
test::test_uid_deletion(&mut db);
|
test::test_uid_deletion(&mut db);
|
||||||
}
|
}
|
||||||
|
@ -718,7 +665,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn subkey_lookup() {
|
fn subkey_lookup() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
|
|
||||||
test::test_subkey_lookup(&mut db);
|
test::test_subkey_lookup(&mut db);
|
||||||
}
|
}
|
||||||
|
@ -726,7 +673,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn kid_lookup() {
|
fn kid_lookup() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
|
|
||||||
test::test_kid_lookup(&mut db);
|
test::test_kid_lookup(&mut db);
|
||||||
}
|
}
|
||||||
|
@ -734,14 +681,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn upload_revoked_tpk() {
|
fn upload_revoked_tpk() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
test::test_upload_revoked_tpk(&mut db);
|
test::test_upload_revoked_tpk(&mut db);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn uid_revocation() {
|
fn uid_revocation() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
|
|
||||||
test::test_uid_revocation(&mut db);
|
test::test_uid_revocation(&mut db);
|
||||||
}
|
}
|
||||||
|
@ -749,7 +696,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn key_reupload() {
|
fn key_reupload() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
|
|
||||||
test::test_reupload(&mut db);
|
test::test_reupload(&mut db);
|
||||||
}
|
}
|
||||||
|
@ -757,7 +704,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn uid_replacement() {
|
fn uid_replacement() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
|
|
||||||
test::test_uid_replacement(&mut db);
|
test::test_uid_replacement(&mut db);
|
||||||
}
|
}
|
||||||
|
@ -765,7 +712,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn uid_stealing() {
|
fn uid_stealing() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
|
|
||||||
test::test_steal_uid(&mut db);
|
test::test_steal_uid(&mut db);
|
||||||
}
|
}
|
||||||
|
@ -773,14 +720,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn uid_unlinking() {
|
fn uid_unlinking() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
test::test_unlink_uid(&mut db);
|
test::test_unlink_uid(&mut db);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn same_email_1() {
|
fn same_email_1() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
|
|
||||||
test::test_same_email_1(&mut db);
|
test::test_same_email_1(&mut db);
|
||||||
}
|
}
|
||||||
|
@ -788,7 +735,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn same_email_2() {
|
fn same_email_2() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
|
|
||||||
test::test_same_email_2(&mut db);
|
test::test_same_email_2(&mut db);
|
||||||
}
|
}
|
||||||
|
@ -796,7 +743,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn reverse_fingerprint_to_path() {
|
fn reverse_fingerprint_to_path() {
|
||||||
let tmpdir = TempDir::new().unwrap();
|
let tmpdir = TempDir::new().unwrap();
|
||||||
let db = Filesystem::new(tmpdir.path()).unwrap();
|
let db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
||||||
|
|
||||||
let fp: Fingerprint =
|
let fp: Fingerprint =
|
||||||
"CBCD8F030588653EEDD7E2659B7DD433F254904A".parse().unwrap();
|
"CBCD8F030588653EEDD7E2659B7DD433F254904A".parse().unwrap();
|
||||||
|
|
|
@ -51,6 +51,9 @@ pub use self::memory::Memory;
|
||||||
mod poly;
|
mod poly;
|
||||||
pub use self::poly::Polymorphic;
|
pub use self::poly::Polymorphic;
|
||||||
|
|
||||||
|
mod stateful_tokens;
|
||||||
|
pub use stateful_tokens::StatefulTokens;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
|
@ -149,8 +152,6 @@ pub trait Database: Sync + Send {
|
||||||
/// read operations to ensure that we return something sane.
|
/// read operations to ensure that we return something sane.
|
||||||
fn lock(&self) -> MutexGuard<()>;
|
fn lock(&self) -> MutexGuard<()>;
|
||||||
|
|
||||||
fn new_verify_token(&self, payload: Verify) -> Result<String>;
|
|
||||||
|
|
||||||
/// Update the data associated with `fpr` with the data in new.
|
/// Update the data associated with `fpr` with the data in new.
|
||||||
///
|
///
|
||||||
/// If new is None, this removes any associated data.
|
/// If new is None, this removes any associated data.
|
||||||
|
@ -224,9 +225,6 @@ pub trait Database: Sync + Send {
|
||||||
fn link_fpr(&self, from: &Fingerprint, to: &Fingerprint) -> Result<()>;
|
fn link_fpr(&self, from: &Fingerprint, to: &Fingerprint) -> Result<()>;
|
||||||
fn unlink_fpr(&self, from: &Fingerprint, to: &Fingerprint) -> Result<()>;
|
fn unlink_fpr(&self, from: &Fingerprint, to: &Fingerprint) -> Result<()>;
|
||||||
|
|
||||||
// (verified uid, fpr)
|
|
||||||
fn pop_verify_token(&self, token: &str) -> Option<Verify>;
|
|
||||||
|
|
||||||
fn by_fpr(&self, fpr: &Fingerprint) -> Option<String>;
|
fn by_fpr(&self, fpr: &Fingerprint) -> Option<String>;
|
||||||
fn by_kid(&self, kid: &KeyID) -> Option<String>;
|
fn by_kid(&self, kid: &KeyID) -> Option<String>;
|
||||||
fn by_email(&self, email: &Email) -> Option<String>;
|
fn by_email(&self, email: &Email) -> Option<String>;
|
||||||
|
@ -397,8 +395,8 @@ pub trait Database: Sync + Send {
|
||||||
self.link_subkeys(&fpr, subkeys)?;
|
self.link_subkeys(&fpr, subkeys)?;
|
||||||
|
|
||||||
let mut tokens = Vec::new();
|
let mut tokens = Vec::new();
|
||||||
for (fp, verify) in unverified_uids.into_iter() {
|
for (email, verify) in unverified_uids.into_iter() {
|
||||||
tokens.push((fp, self.new_verify_token(verify)?));
|
tokens.push((email, serde_json::to_string(&verify)?));
|
||||||
}
|
}
|
||||||
Ok(tokens)
|
Ok(tokens)
|
||||||
}
|
}
|
||||||
|
@ -411,46 +409,43 @@ pub trait Database: Sync + Send {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
fn verify_token(
|
fn verify_token(
|
||||||
&self, token: &str,
|
&self, token_str: &str,
|
||||||
) -> Result<Option<(Email, Fingerprint)>> {
|
) -> Result<Option<(Email, Fingerprint)>> {
|
||||||
let _ = self.lock();
|
let _ = self.lock();
|
||||||
|
|
||||||
match self.pop_verify_token(token) {
|
let Verify { created, packets, fpr, email } = serde_json::from_str(&token_str)?;
|
||||||
Some(Verify { created, packets, fpr, email }) => {
|
|
||||||
let now = time::now().to_timespec().sec;
|
|
||||||
if created > now || now - created > 3 * 3600 {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.by_fpr(&fpr) {
|
let now = time::now().to_timespec().sec;
|
||||||
Some(old) => {
|
if created > now || now - created > 3 * 3600 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
let tpk = TPK::from_bytes(old.as_bytes()).unwrap();
|
match self.by_fpr(&fpr) {
|
||||||
let packet_pile = PacketPile::from_bytes(&packets)
|
Some(old) => {
|
||||||
.unwrap().into_children().collect::<Vec<_>>();
|
|
||||||
let new = tpk.merge_packets(packet_pile).unwrap();
|
let tpk = TPK::from_bytes(old.as_bytes()).unwrap();
|
||||||
|
let packet_pile = PacketPile::from_bytes(&packets)
|
||||||
|
.unwrap().into_children().collect::<Vec<_>>();
|
||||||
|
let new = tpk.merge_packets(packet_pile).unwrap();
|
||||||
|
|
||||||
|
|
||||||
let mut buf = std::io::Cursor::new(vec![]);
|
let mut buf = std::io::Cursor::new(vec![]);
|
||||||
{
|
{
|
||||||
let mut armor_writer = Writer::new(&mut buf, Kind::PublicKey,
|
let mut armor_writer = Writer::new(&mut buf, Kind::PublicKey,
|
||||||
&[][..])?;
|
&[][..])?;
|
||||||
|
|
||||||
armor_writer.write_all(&Self::tpk_into_bytes(&new).unwrap())?;
|
armor_writer.write_all(&Self::tpk_into_bytes(&new).unwrap())?;
|
||||||
};
|
};
|
||||||
let armored = String::from_utf8_lossy(buf.get_ref());
|
let armored = String::from_utf8_lossy(buf.get_ref());
|
||||||
|
|
||||||
self.update(&fpr, Some(armored.into_owned()))?;
|
self.update(&fpr, Some(armored.into_owned()))?;
|
||||||
self.link_email(&email, &fpr)?;
|
self.link_email(&email, &fpr)?;
|
||||||
return Ok(Some((email.clone(), fpr.clone())));
|
return Ok(Some((email.clone(), fpr.clone())));
|
||||||
}
|
}
|
||||||
|
|
||||||
None => {
|
None => {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => Err(failure::err_msg("No such token")),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use {Database, Verify, Query};
|
use {Database, Query};
|
||||||
use types::{Email, Fingerprint, KeyID};
|
use types::{Email, Fingerprint, KeyID};
|
||||||
use sync::MutexGuard;
|
use sync::MutexGuard;
|
||||||
use Result;
|
use Result;
|
||||||
|
@ -15,7 +15,6 @@ pub struct Memory {
|
||||||
fpr_links: Mutex<HashMap<Fingerprint, Fingerprint>>,
|
fpr_links: Mutex<HashMap<Fingerprint, Fingerprint>>,
|
||||||
email: Mutex<HashMap<Email, Fingerprint>>,
|
email: Mutex<HashMap<Email, Fingerprint>>,
|
||||||
kid: Mutex<HashMap<KeyID, Fingerprint>>,
|
kid: Mutex<HashMap<KeyID, Fingerprint>>,
|
||||||
verify_token: Mutex<HashMap<String, Verify>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Memory {
|
impl Default for Memory {
|
||||||
|
@ -26,7 +25,6 @@ impl Default for Memory {
|
||||||
fpr_links: Mutex::new(HashMap::default()),
|
fpr_links: Mutex::new(HashMap::default()),
|
||||||
kid: Mutex::new(HashMap::default()),
|
kid: Mutex::new(HashMap::default()),
|
||||||
email: Mutex::new(HashMap::default()),
|
email: Mutex::new(HashMap::default()),
|
||||||
verify_token: Mutex::new(HashMap::default()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,13 +34,6 @@ impl Database for Memory {
|
||||||
self.update_lock.lock().into()
|
self.update_lock.lock().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_verify_token(&self, payload: Verify) -> Result<String> {
|
|
||||||
let token = Self::new_token();
|
|
||||||
|
|
||||||
self.verify_token.lock().insert(token.clone(), payload);
|
|
||||||
Ok(token)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(
|
fn update(
|
||||||
&self, fpr: &Fingerprint, new: Option<String>,
|
&self, fpr: &Fingerprint, new: Option<String>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
@ -103,11 +94,6 @@ impl Database for Memory {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// (verified uid, fpr)
|
|
||||||
fn pop_verify_token(&self, token: &str) -> Option<Verify> {
|
|
||||||
self.verify_token.lock().remove(token)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn by_fpr(&self, fpr: &Fingerprint) -> Option<String> {
|
fn by_fpr(&self, fpr: &Fingerprint) -> Option<String> {
|
||||||
let fprs = self.fpr.lock();
|
let fprs = self.fpr.lock();
|
||||||
let links = self.fpr_links.lock();
|
let links = self.fpr_links.lock();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use {Database, Filesystem, Memory, Verify, Query};
|
use {Database, Filesystem, Memory, Query};
|
||||||
use Result;
|
use Result;
|
||||||
use types::{Email, Fingerprint, KeyID};
|
use types::{Email, Fingerprint, KeyID};
|
||||||
use sync::MutexGuard;
|
use sync::MutexGuard;
|
||||||
|
@ -18,13 +18,6 @@ impl Database for Polymorphic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_verify_token(&self, payload: Verify) -> Result<String> {
|
|
||||||
match self {
|
|
||||||
&Polymorphic::Memory(ref db) => db.new_verify_token(payload),
|
|
||||||
&Polymorphic::Filesystem(ref db) => db.new_verify_token(payload),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(
|
fn update(
|
||||||
&self, fpr: &Fingerprint, new: Option<String>,
|
&self, fpr: &Fingerprint, new: Option<String>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
@ -99,13 +92,6 @@ impl Database for Polymorphic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_verify_token(&self, token: &str) -> Option<Verify> {
|
|
||||||
match self {
|
|
||||||
&Polymorphic::Memory(ref db) => db.pop_verify_token(token),
|
|
||||||
&Polymorphic::Filesystem(ref db) => db.pop_verify_token(token),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn by_fpr(&self, fpr: &Fingerprint) -> Option<String> {
|
fn by_fpr(&self, fpr: &Fingerprint) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
&Polymorphic::Memory(ref db) => db.by_fpr(fpr),
|
&Polymorphic::Memory(ref db) => db.by_fpr(fpr),
|
||||||
|
|
55
database/src/stateful_tokens.rs
Normal file
55
database/src/stateful_tokens.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use std::io::{Read,Write};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::fs::{create_dir_all, remove_file, File};
|
||||||
|
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use Result;
|
||||||
|
|
||||||
|
pub struct StatefulTokens {
|
||||||
|
state_dir: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatefulTokens {
|
||||||
|
pub fn new(state_dir: impl Into<PathBuf>) -> Result<Self> {
|
||||||
|
let state_dir = state_dir.into();
|
||||||
|
create_dir_all(&state_dir)?;
|
||||||
|
|
||||||
|
info!("Opened stateful token store");
|
||||||
|
info!("state_dir: '{}'", state_dir.display());
|
||||||
|
|
||||||
|
Ok(StatefulTokens { state_dir })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_token(&self, token_type: &str, payload: &[u8]) -> Result<String> {
|
||||||
|
use rand::distributions::Alphanumeric;
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
// samples from [a-zA-Z0-9]
|
||||||
|
// 43 chars ~ 256 bit
|
||||||
|
let name: String = rng.sample_iter(&Alphanumeric).take(43).collect();
|
||||||
|
let dir = self.state_dir.join(token_type);
|
||||||
|
create_dir_all(&dir)?;
|
||||||
|
|
||||||
|
let mut fd = File::create(dir.join(&name))?;
|
||||||
|
fd.write_all(payload)?;
|
||||||
|
|
||||||
|
Ok(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_token(&self, token_type: &str, token: &str) -> Result<String> {
|
||||||
|
let path = self.state_dir.join(token_type).join(token);
|
||||||
|
let buf = {
|
||||||
|
let mut fd = File::open(&path)?;
|
||||||
|
let mut buf = Vec::default();
|
||||||
|
|
||||||
|
fd.read_to_end(&mut buf)?;
|
||||||
|
buf.into_boxed_slice()
|
||||||
|
};
|
||||||
|
|
||||||
|
remove_file(path)?;
|
||||||
|
|
||||||
|
Ok(str::from_utf8(&buf)?.to_string())
|
||||||
|
}
|
||||||
|
}
|
|
@ -89,8 +89,8 @@ pub fn test_uid_verification<D: Database>(db: &mut D) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify 1st uid again
|
// this operation is idempotent - let's try again!
|
||||||
assert!(db.verify_token(&tokens[0].1).is_err());
|
assert!(db.verify_token(&tokens[0].1).unwrap().is_some());
|
||||||
|
|
||||||
{
|
{
|
||||||
// fetch by fpr
|
// fetch by fpr
|
||||||
|
|
|
@ -108,10 +108,12 @@ impl<'a, 'r> FromRequest<'a, 'r> for Hkp {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/pks/add", data = "<data>")]
|
#[post("/pks/add", data = "<data>")]
|
||||||
pub fn pks_add(db: rocket::State<Polymorphic>, cont_type: &ContentType,
|
pub fn pks_add(
|
||||||
data: Data)
|
db: rocket::State<Polymorphic>,
|
||||||
-> MyResponse {
|
cont_type: &ContentType,
|
||||||
match upload::handle_upload(db, cont_type, data, None) {
|
data: Data,
|
||||||
|
) -> MyResponse {
|
||||||
|
match upload::handle_upload_without_verify(db, cont_type, data) {
|
||||||
Ok(_) => MyResponse::plain("Ok".into()),
|
Ok(_) => MyResponse::plain("Ok".into()),
|
||||||
Err(err) => MyResponse::ise(err),
|
Err(err) => MyResponse::ise(err),
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub mod upload;
|
||||||
use mail;
|
use mail;
|
||||||
use tokens;
|
use tokens;
|
||||||
|
|
||||||
use database::{Database, Polymorphic, Query};
|
use database::{Database, Polymorphic, Query, StatefulTokens};
|
||||||
use database::types::{Email, Fingerprint, KeyID};
|
use database::types::{Email, Fingerprint, KeyID};
|
||||||
use Result;
|
use Result;
|
||||||
|
|
||||||
|
@ -66,20 +66,12 @@ impl MyResponse {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX needs fixing for keys_dir!
|
pub fn x_accel_redirect(x_accel_path: String, fp: &Fingerprint) -> Self {
|
||||||
pub fn x_accel_redirect(path: PathBuf, fp: &Fingerprint) -> Self {
|
|
||||||
use rocket::http::hyper::header::{ContentDisposition, DispositionType,
|
use rocket::http::hyper::header::{ContentDisposition, DispositionType,
|
||||||
DispositionParam, Charset};
|
DispositionParam, Charset};
|
||||||
// The path is relative to our base directory, but we need to
|
|
||||||
// get it relative to base/public.
|
|
||||||
let mut path = path.into_os_string().into_string().expect("valid UTF8");
|
|
||||||
// Drop the first component.
|
|
||||||
assert!(path.starts_with("public/"));
|
|
||||||
path.drain(..6);
|
|
||||||
|
|
||||||
MyResponse::XAccelRedirect(
|
MyResponse::XAccelRedirect(
|
||||||
"",
|
"",
|
||||||
Header::new("X-Accel-Redirect", path),
|
Header::new("X-Accel-Redirect", x_accel_path),
|
||||||
ContentDisposition {
|
ContentDisposition {
|
||||||
disposition: DispositionType::Attachment,
|
disposition: DispositionType::Attachment,
|
||||||
parameters: vec![
|
parameters: vec![
|
||||||
|
@ -91,6 +83,7 @@ impl MyResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ise(e: failure::Error) -> Self {
|
pub fn ise(e: failure::Error) -> Self {
|
||||||
|
println!("Internal error: {:?}", e);
|
||||||
let ctx = templates::FiveHundred{
|
let ctx = templates::FiveHundred{
|
||||||
error: format!("{}", e),
|
error: format!("{}", e),
|
||||||
version: env!("VERGEN_SEMVER").to_string(),
|
version: env!("VERGEN_SEMVER").to_string(),
|
||||||
|
@ -172,9 +165,6 @@ mod templates {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HagridState {
|
pub struct HagridState {
|
||||||
/// State directory, used internally by hagrid
|
|
||||||
state_dir: PathBuf,
|
|
||||||
|
|
||||||
/// Assets directory, mounted to /assets, served by hagrid or nginx
|
/// Assets directory, mounted to /assets, served by hagrid or nginx
|
||||||
assets_dir: PathBuf,
|
assets_dir: PathBuf,
|
||||||
|
|
||||||
|
@ -202,8 +192,9 @@ fn key_to_response<'a>(state: rocket::State<HagridState>,
|
||||||
|
|
||||||
if machine_readable {
|
if machine_readable {
|
||||||
if state.x_accel_redirect {
|
if state.x_accel_redirect {
|
||||||
if let Some(path) = db.lookup_path(&query) {
|
if let Some(key_path) = db.lookup_path(&query) {
|
||||||
return MyResponse::x_accel_redirect(path, &fp);
|
let x_accel_path = state.keys_dir.join(&key_path).to_string_lossy().to_string();
|
||||||
|
return MyResponse::x_accel_redirect(x_accel_path, &fp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +230,7 @@ fn key_has_uids(state: &HagridState, db: &Polymorphic, query: &Query)
|
||||||
use sequoia_openpgp::Packet;
|
use sequoia_openpgp::Packet;
|
||||||
use sequoia_openpgp::parse::{Parse, PacketParser, PacketParserResult};
|
use sequoia_openpgp::parse::{Parse, PacketParser, PacketParserResult};
|
||||||
let mut ppr = match db.lookup_path(query) {
|
let mut ppr = match db.lookup_path(query) {
|
||||||
Some(path) => PacketParser::from_file(&state.state_dir.join(path))?,
|
Some(path) => PacketParser::from_file(&state.keys_dir.join(path))?,
|
||||||
None => return Err(failure::err_msg("key vanished")),
|
None => return Err(failure::err_msg("key vanished")),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -290,10 +281,27 @@ fn vks_v1_by_keyid(state: rocket::State<HagridState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/publish/<token>")]
|
#[get("/publish/<token>")]
|
||||||
fn publish_verify(db: rocket::State<Polymorphic>,
|
fn publish_verify(
|
||||||
token: String) -> MyResponse {
|
db: rocket::State<Polymorphic>,
|
||||||
match db.verify_token(&token) {
|
token_service: rocket::State<StatefulTokens>,
|
||||||
Ok(Some((userid, _fpr))) => {
|
token: String,
|
||||||
|
) -> MyResponse {
|
||||||
|
match publish_verify_or_fail(db, token_service, token) {
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(e) => MyResponse::ise(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn publish_verify_or_fail(
|
||||||
|
db: rocket::State<Polymorphic>,
|
||||||
|
token_service: rocket::State<StatefulTokens>,
|
||||||
|
token: String,
|
||||||
|
) -> Result<MyResponse> {
|
||||||
|
println!("hi");
|
||||||
|
let payload = token_service.pop_token("verify", &token)?;
|
||||||
|
|
||||||
|
match db.verify_token(&payload)? {
|
||||||
|
Some((userid, _fpr)) => {
|
||||||
let context = templates::Verify {
|
let context = templates::Verify {
|
||||||
verified: true,
|
verified: true,
|
||||||
userid: userid.to_string(),
|
userid: userid.to_string(),
|
||||||
|
@ -301,10 +309,9 @@ fn publish_verify(db: rocket::State<Polymorphic>,
|
||||||
commit: env!("VERGEN_SHA_SHORT").to_string(),
|
commit: env!("VERGEN_SHA_SHORT").to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
MyResponse::ok("publish-result", context)
|
Ok(MyResponse::ok("publish-result", context))
|
||||||
}
|
}
|
||||||
Ok(None) => MyResponse::not_found(Some("generic-error"), None),
|
None => Ok(MyResponse::not_found(Some("generic-error"), None)),
|
||||||
Err(e) => MyResponse::ise(e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,13 +372,15 @@ fn rocket_factory(rocket: rocket::Rocket) -> Result<rocket::Rocket> {
|
||||||
|
|
||||||
let db_service = configure_db_service(rocket.config())?;
|
let db_service = configure_db_service(rocket.config())?;
|
||||||
let hagrid_state = configure_hagrid_state(rocket.config())?;
|
let hagrid_state = configure_hagrid_state(rocket.config())?;
|
||||||
let token_service = configure_token_service(rocket.config())?;
|
let stateful_token_service = configure_stateful_token_service(rocket.config())?;
|
||||||
|
let stateless_token_service = configure_stateless_token_service(rocket.config())?;
|
||||||
let mail_service = configure_mail_service(rocket.config())?;
|
let mail_service = configure_mail_service(rocket.config())?;
|
||||||
|
|
||||||
Ok(rocket
|
Ok(rocket
|
||||||
.attach(Template::fairing())
|
.attach(Template::fairing())
|
||||||
.manage(hagrid_state)
|
.manage(hagrid_state)
|
||||||
.manage(token_service)
|
.manage(stateless_token_service)
|
||||||
|
.manage(stateful_token_service)
|
||||||
.manage(mail_service)
|
.manage(mail_service)
|
||||||
.manage(db_service)
|
.manage(db_service)
|
||||||
.mount("/", routes)
|
.mount("/", routes)
|
||||||
|
@ -382,30 +391,32 @@ fn configure_db_service(config: &Config) -> Result<Polymorphic> {
|
||||||
use database::{Filesystem, Polymorphic};
|
use database::{Filesystem, Polymorphic};
|
||||||
|
|
||||||
let keys_dir: PathBuf = config.get_str("keys_dir")?.into();
|
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 tmp_dir: PathBuf = config.get_str("tmp_dir")?.into();
|
||||||
|
|
||||||
let fs_db = Filesystem::new(keys_dir, state_dir, tmp_dir)?;
|
let fs_db = Filesystem::new(keys_dir, tmp_dir)?;
|
||||||
Ok(Polymorphic::Filesystem(fs_db))
|
Ok(Polymorphic::Filesystem(fs_db))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure_hagrid_state(config: &Config) -> Result<HagridState> {
|
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 assets_dir: PathBuf = config.get_str("assets_dir")?.into();
|
||||||
let keys_dir: PathBuf = config.get_str("keys_dir")?.into();
|
let keys_dir: PathBuf = config.get_str("keys_dir")?.into();
|
||||||
|
|
||||||
// State
|
// State
|
||||||
let base_uri = config.get_str("base-URI")?.to_string();
|
let base_uri = config.get_str("base-URI")?.to_string();
|
||||||
Ok(HagridState {
|
Ok(HagridState {
|
||||||
state_dir,
|
|
||||||
assets_dir,
|
assets_dir,
|
||||||
keys_dir,
|
keys_dir: keys_dir,
|
||||||
base_uri: base_uri.clone(),
|
base_uri: base_uri.clone(),
|
||||||
x_accel_redirect: config.get_bool("x-accel-redirect")?,
|
x_accel_redirect: config.get_bool("x-accel-redirect")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure_token_service(config: &Config) -> Result<tokens::Service> {
|
fn configure_stateful_token_service(config: &Config) -> Result<database::StatefulTokens> {
|
||||||
|
let state_dir: PathBuf = config.get_str("state_dir")?.into();
|
||||||
|
database::StatefulTokens::new(state_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_stateless_token_service(config: &Config) -> Result<tokens::Service> {
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
let secret = config.get_str("token_secret")?.to_string();
|
let secret = config.get_str("token_secret")?.to_string();
|
||||||
|
|
|
@ -8,7 +8,7 @@ use multipart::server::Multipart;
|
||||||
use rocket::http::ContentType;
|
use rocket::http::ContentType;
|
||||||
use rocket::Data;
|
use rocket::Data;
|
||||||
|
|
||||||
use database::{Database, Polymorphic};
|
use database::{Database, Polymorphic, StatefulTokens};
|
||||||
use mail;
|
use mail;
|
||||||
use web::MyResponse;
|
use web::MyResponse;
|
||||||
|
|
||||||
|
@ -45,19 +45,29 @@ pub fn publish(guide: bool) -> MyResponse {
|
||||||
|
|
||||||
#[post("/vks/v1/publish", data = "<data>")]
|
#[post("/vks/v1/publish", data = "<data>")]
|
||||||
pub fn vks_v1_publish_post(
|
pub fn vks_v1_publish_post(
|
||||||
db: rocket::State<Polymorphic>, cont_type: &ContentType, data: Data,
|
db: rocket::State<Polymorphic>,
|
||||||
mail_service: rocket::State<mail::Service>
|
mail_service: rocket::State<mail::Service>,
|
||||||
|
token_service: rocket::State<StatefulTokens>,
|
||||||
|
cont_type: &ContentType,
|
||||||
|
data: Data,
|
||||||
) -> MyResponse {
|
) -> MyResponse {
|
||||||
match handle_upload(db, cont_type, data, Some(mail_service)) {
|
match handle_upload(db, cont_type, data, Some((mail_service, token_service))) {
|
||||||
Ok(ok) => ok,
|
Ok(ok) => ok,
|
||||||
Err(err) => MyResponse::ise(err),
|
Err(err) => MyResponse::ise(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn handle_upload_without_verify(
|
||||||
|
db: rocket::State<Polymorphic>,
|
||||||
|
cont_type: &ContentType,
|
||||||
|
data: Data,
|
||||||
|
) -> Result<MyResponse> {
|
||||||
|
handle_upload(db, cont_type, data, None)
|
||||||
|
}
|
||||||
|
|
||||||
// signature requires the request to have a `Content-Type`
|
// signature requires the request to have a `Content-Type`
|
||||||
pub fn handle_upload(
|
pub fn handle_upload(
|
||||||
db: rocket::State<Polymorphic>, cont_type: &ContentType, data: Data,
|
db: rocket::State<Polymorphic>, cont_type: &ContentType, data: Data,
|
||||||
mail_service: Option<rocket::State<mail::Service>>
|
services: Option<(rocket::State<mail::Service>, rocket::State<StatefulTokens>)>,
|
||||||
) -> Result<MyResponse> {
|
) -> Result<MyResponse> {
|
||||||
if cont_type.is_form_data() {
|
if cont_type.is_form_data() {
|
||||||
// multipart/form-data
|
// multipart/form-data
|
||||||
|
@ -70,7 +80,7 @@ pub fn handle_upload(
|
||||||
boundary param not provided"))),
|
boundary param not provided"))),
|
||||||
};
|
};
|
||||||
|
|
||||||
process_upload(boundary, data, db.inner(), mail_service)
|
process_upload(boundary, data, db.inner(), services)
|
||||||
} else if cont_type.is_form() {
|
} else if cont_type.is_form() {
|
||||||
use rocket::request::FormItems;
|
use rocket::request::FormItems;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
@ -93,7 +103,7 @@ pub fn handle_upload(
|
||||||
return process_key(
|
return process_key(
|
||||||
Cursor::new(decoded_value.as_bytes()),
|
Cursor::new(decoded_value.as_bytes()),
|
||||||
&db,
|
&db,
|
||||||
mail_service,
|
services,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => { /* skip */ }
|
_ => { /* skip */ }
|
||||||
|
@ -110,29 +120,30 @@ pub fn handle_upload(
|
||||||
|
|
||||||
fn process_upload(
|
fn process_upload(
|
||||||
boundary: &str, data: Data, db: &Polymorphic,
|
boundary: &str, data: Data, db: &Polymorphic,
|
||||||
mail_service: Option<rocket::State<mail::Service>>,
|
services: Option<(rocket::State<mail::Service>, rocket::State<StatefulTokens>)>,
|
||||||
) -> Result<MyResponse> {
|
) -> Result<MyResponse> {
|
||||||
// saves all fields, any field longer than 10kB goes to a temporary directory
|
// saves all fields, any field longer than 10kB goes to a temporary directory
|
||||||
// Entries could implement FromData though that would give zero control over
|
// Entries could implement FromData though that would give zero control over
|
||||||
// how the files are saved; Multipart would be a good impl candidate though
|
// how the files are saved; Multipart would be a good impl candidate though
|
||||||
match Multipart::with_body(data.open().take(UPLOAD_LIMIT), boundary).save().temp() {
|
match Multipart::with_body(data.open().take(UPLOAD_LIMIT), boundary).save().temp() {
|
||||||
Full(entries) => {
|
Full(entries) => {
|
||||||
process_multipart(entries, db, mail_service)
|
process_multipart(entries, db, services)
|
||||||
}
|
}
|
||||||
Partial(partial, _) => {
|
Partial(partial, _) => {
|
||||||
process_multipart(partial.entries, db, mail_service)
|
process_multipart(partial.entries, db, services)
|
||||||
}
|
}
|
||||||
Error(err) => Err(err.into())
|
Error(err) => Err(err.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_multipart(entries: Entries, db: &Polymorphic,
|
fn process_multipart(
|
||||||
mail_service: Option<rocket::State<mail::Service>>)
|
entries: Entries, db: &Polymorphic,
|
||||||
-> Result<MyResponse> {
|
services: Option<(rocket::State<mail::Service>, rocket::State<StatefulTokens>)>,
|
||||||
|
) -> Result<MyResponse> {
|
||||||
match entries.fields.get("keytext") {
|
match entries.fields.get("keytext") {
|
||||||
Some(ent) if ent.len() == 1 => {
|
Some(ent) if ent.len() == 1 => {
|
||||||
let reader = ent[0].data.readable()?;
|
let reader = ent[0].data.readable()?;
|
||||||
process_key(reader, db, mail_service)
|
process_key(reader, db, services)
|
||||||
}
|
}
|
||||||
Some(_) =>
|
Some(_) =>
|
||||||
Ok(MyResponse::bad_request(
|
Ok(MyResponse::bad_request(
|
||||||
|
@ -144,7 +155,9 @@ fn process_multipart(entries: Entries, db: &Polymorphic,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_key<R>(
|
fn process_key<R>(
|
||||||
reader: R, db: &Polymorphic, mail_service: Option<rocket::State<mail::Service>>,
|
reader: R,
|
||||||
|
db: &Polymorphic,
|
||||||
|
services: Option<(rocket::State<mail::Service>, rocket::State<StatefulTokens>)>,
|
||||||
) -> Result<MyResponse>
|
) -> Result<MyResponse>
|
||||||
where
|
where
|
||||||
R: Read,
|
R: Read,
|
||||||
|
@ -173,10 +186,11 @@ where
|
||||||
|
|
||||||
let mut results: Vec<String> = vec!();
|
let mut results: Vec<String> = vec!();
|
||||||
for tpk in tpks {
|
for tpk in tpks {
|
||||||
let tokens = db.merge_or_publish(&tpk)?;
|
let verification_strings = db.merge_or_publish(&tpk)?;
|
||||||
|
|
||||||
if let Some(ref mail_service) = mail_service {
|
if let Some((ref mail_service, ref token_service)) = services {
|
||||||
for (email, token) in tokens {
|
for (email, data) in verification_strings {
|
||||||
|
let token = token_service.new_token("verify", data.as_bytes())?;
|
||||||
mail_service.send_verification(
|
mail_service.send_verification(
|
||||||
&tpk,
|
&tpk,
|
||||||
&email,
|
&email,
|
||||||
|
|
Loading…
Reference in a new issue