diff options
-rw-r--r-- | store/src/backend.rs | 24 | ||||
-rw-r--r-- | store/src/lib.rs | 124 | ||||
-rw-r--r-- | store/src/store_protocol.capnp | 7 | ||||
-rw-r--r-- | tool/src/main.rs | 22 |
4 files changed, 173 insertions, 4 deletions
diff --git a/store/src/backend.rs b/store/src/backend.rs index 88b85177..6756b485 100644 --- a/store/src/backend.rs +++ b/store/src/backend.rs @@ -212,6 +212,16 @@ impl node::store::Server for StoreServer { .from_server::<capnp_rpc::Server>())); Promise::ok(()) } + + fn delete(&mut self, + _: node::store::DeleteParams, + mut results: node::store::DeleteResults) + -> Promise<(), capnp::Error> { + bind_results!(results); + sry!(self.c.borrow().execute("DELETE FROM stores WHERE id = ?1", + &[&self.id])); + Promise::ok(()) + } } struct BindingServer { @@ -328,6 +338,16 @@ impl node::binding::Server for BindingServer { Promise::ok(()) } + fn delete(&mut self, + _: node::binding::DeleteParams, + mut results: node::binding::DeleteResults) + -> Promise<(), capnp::Error> { + bind_results!(results); + sry!(self.c.borrow().execute("DELETE FROM bindings WHERE id = ?1", + &[&self.id])); + Promise::ok(()) + } + fn register_encryption(&mut self, _: node::binding::RegisterEncryptionParams, mut results: node::binding::RegisterEncryptionResults) @@ -531,8 +551,8 @@ CREATE TABLE bindings ( verification_last DEFAULT 0, UNIQUE(store, label), - FOREIGN KEY (store) REFERENCES stores(id), - FOREIGN KEY (key) REFERENCES keys(id)); + FOREIGN KEY (store) REFERENCES stores(id) ON DELETE CASCADE, + FOREIGN KEY (key) REFERENCES keys(id) ON DELETE CASCADE); CREATE TABLE keys ( id INTEGER PRIMARY KEY, diff --git a/store/src/lib.rs b/store/src/lib.rs index 5d0dc357..e772476c 100644 --- a/store/src/lib.rs +++ b/store/src/lib.rs @@ -237,6 +237,37 @@ impl Store { let binding = make_request!(self.core.borrow_mut(), request)?; Ok(Binding::new(self, label, binding)) } + + /// Deletes this store. + /// + /// # Example + /// + /// ``` + /// # extern crate openpgp; + /// # #[macro_use] extern crate sequoia_core; + /// # extern crate sequoia_store; + /// # use openpgp::Fingerprint; + /// # use sequoia_core::Context; + /// # use sequoia_store::{Store, Result, Error}; + /// # fn main() { f().unwrap(); } + /// # fn f() -> Result<()> { + /// # let ctx = Context::configure("org.sequoia-pgp.demo.store") + /// # .ephemeral().build()?; + /// let store = Store::open(&ctx, "default")?; + /// let fp = Fingerprint::from_bytes(b"bbbbbbbbbbbbbbbbbbbb"); + /// store.add("Mister B.", &fp)?; + /// store.delete()?; + /// // ... + /// let store = Store::open(&ctx, "default")?; + /// let binding = store.lookup("Mister B."); + /// assert_match!(Err(Error::NotFound) = binding); + /// # Ok(()) + /// # } + /// ``` + pub fn delete(self) -> Result<()> { + let request = self.store.delete_request(); + make_request_map!(self.core.borrow_mut(), request, |_| Ok(())) + } } /// Represents an entry in a Store. @@ -424,6 +455,35 @@ impl<'a> Binding<'a> { |data| TPK::from_bytes(data).map_err(|e| e.into())) } + /// Deletes this binding. + /// + /// # Example + /// + /// ``` + /// # extern crate openpgp; + /// # #[macro_use] extern crate sequoia_core; + /// # extern crate sequoia_store; + /// # use openpgp::Fingerprint; + /// # use sequoia_core::Context; + /// # use sequoia_store::{Store, Result, Error}; + /// # fn main() { f().unwrap(); } + /// # fn f() -> Result<()> { + /// # let ctx = Context::configure("org.sequoia-pgp.demo.store") + /// # .ephemeral().build()?; + /// let store = Store::open(&ctx, "default")?; + /// let fp = Fingerprint::from_bytes(b"bbbbbbbbbbbbbbbbbbbb"); + /// let binding = store.add("Mister B.", &fp)?; + /// binding.delete()?; + /// let binding = store.lookup("Mister B."); + /// assert_match!(Err(Error::NotFound) = binding); + /// # Ok(()) + /// # } + /// ``` + pub fn delete(self) -> Result<()> { + let request = self.binding.delete_request(); + make_request_map!(self.store.core.borrow_mut(), request, |_| Ok(())) + } + fn register_encryption(&self) -> Result<Stats> { #![allow(dead_code)] // XXX use make_stats_request!( @@ -448,6 +508,12 @@ pub struct Key<'a> { key: node::key::Client, } +impl<'a> fmt::Debug for Key<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Key {{ }}") + } +} + impl<'a> Key<'a> { fn new(store: &'a Store, key: node::key::Client) -> Self { Key{store: store, key: key} @@ -700,5 +766,63 @@ mod store_test { let r = binding.import(&tpk); assert_match!(Err(Error::Conflict) = r); } + + + #[test] + fn delete_store_twice() { + let ctx = core::Context::configure("org.sequoia-pgp.tests") + .ephemeral() + .network_policy(core::NetworkPolicy::Offline) + .build().unwrap(); + let s0 = Store::open(&ctx, "default").unwrap(); + let s1 = Store::open(&ctx, "default").unwrap(); + s0.delete().unwrap(); + s1.delete().unwrap(); + } + + #[test] + fn delete_store_then_use() { + let ctx = core::Context::configure("org.sequoia-pgp.tests") + .ephemeral() + .network_policy(core::NetworkPolicy::Offline) + .build().unwrap(); + let s0 = Store::open(&ctx, "default").unwrap(); + let s1 = Store::open(&ctx, "default").unwrap(); + s0.delete().unwrap(); + let binding = s1.lookup("Foobarbaz"); + assert_match!(Err(Error::NotFound) = binding); + let fp = Fingerprint::from_bytes(b"bbbbbbbbbbbbbbbbbbbb"); + let binding = s1.add("Mister B.", &fp); + assert_match!(Err(Error::NotFound) = binding); + } + + #[test] + fn delete_binding_twice() { + let ctx = core::Context::configure("org.sequoia-pgp.tests") + .ephemeral() + .network_policy(core::NetworkPolicy::Offline) + .build().unwrap(); + let store = Store::open(&ctx, "default").unwrap(); + let fp = Fingerprint::from_bytes(b"bbbbbbbbbbbbbbbbbbbb"); + let b0 = store.add("Mister B.", &fp).unwrap(); + let b1 = store.lookup("Mister B.").unwrap(); + b0.delete().unwrap(); + b1.delete().unwrap(); + } + + #[test] + fn delete_binding_then_use() { + let ctx = core::Context::configure("org.sequoia-pgp.tests") + .ephemeral() + .network_policy(core::NetworkPolicy::Offline) + .build().unwrap(); + let store = Store::open(&ctx, "default").unwrap(); + let fp = Fingerprint::from_bytes(b"bbbbbbbbbbbbbbbbbbbb"); + let b0 = store.add("Mister B.", &fp).unwrap(); + let b1 = store.lookup("Mister B.").unwrap(); + b0.delete().unwrap(); + assert_match!(Err(Error::NotFound) = b1.stats()); + assert_match!(Err(Error::NotFound) = b1.key()); + } } diff --git a/store/src/store_protocol.capnp b/store/src/store_protocol.capnp index 74f162cf..f29565da 100644 --- a/store/src/store_protocol.capnp +++ b/store/src/store_protocol.capnp @@ -7,7 +7,7 @@ interface Node { interface Store { add @0 (label: Text, fingerprint: Text) -> (result: Result(Binding)); lookup @1 (label: Text) -> (result: Result(Binding)); - delete @2 (); + delete @2 () -> (result: Result(Unit)); #iterate @3 (id: UInt32) -> (result: Result(Binding)); } @@ -15,7 +15,7 @@ interface Node { stats @0 () -> (result: Result(Stats)); key @1 () -> (result: Result(Key)); import @2 (key: Data, force: Bool) -> (result: Result(Data)); - delete @3 (); + delete @3 () -> (result: Result(Unit)); registerEncryption @4 () -> (result: Result(Stats)); registerVerification @5 () -> (result: Result(Stats)); } @@ -26,6 +26,9 @@ interface Node { import @2 (key: Data) -> (result: Result(Data)); } + # Unit struct. Useful with Result. + struct Unit {} + struct Stats { created @0 :Int64; updated @1 :Int64; diff --git a/tool/src/main.rs b/tool/src/main.rs index 0d06266c..dd791252 100644 --- a/tool/src/main.rs +++ b/tool/src/main.rs @@ -147,6 +147,14 @@ fn real_main() -> Result<()> { .long("armor") .short("A") .help("Write armored data to file"))) + .subcommand(SubCommand::with_name("delete") + .about("Deletes bindings or stores") + .arg(Arg::with_name("the-store") + .long("the-store") + .help("Delete the whole store")) + .arg(Arg::with_name("label") + .value_name("LABEL") + .help("Delete binding with this label"))) .subcommand(SubCommand::with_name("stats") .about("Get stats for the given label") .arg(Arg::with_name("label").value_name("LABEL") @@ -298,6 +306,20 @@ fn real_main() -> Result<()> { tpk.serialize(&mut output) .expect("Failed to write the key"); }, + ("delete", Some(m)) => { + if m.is_present("label") == m.is_present("the-store") { + eprintln!("Please specify either a label or --the-store."); + exit(1); + } + + if m.is_present("the-store") { + store.delete().expect("Failed to delete the store"); + } else { + let binding = store.lookup(m.value_of("label").unwrap()) + .expect("Failed to get key"); + binding.delete().expect("Failed to delete the binding"); + } + }, ("stats", Some(m)) => { let binding = store.lookup(m.value_of("label").unwrap()) .expect("Failed to get key"); |