diff options
| author | Ellie Huxtable <ellie@elliehuxtable.com> | 2024-02-02 14:27:23 +0000 |
|---|---|---|
| committer | Ellie Huxtable <ellie@elliehuxtable.com> | 2024-02-02 18:01:09 +0000 |
| commit | 754f17ddb4969973b9332a5324af42886e503c0f (patch) | |
| tree | 17ed3498466321025ff65d87e0b24ec40ce92232 /atuin-client/src/record | |
| parent | feat: add verify command to local store (diff) | |
| download | atuin-754f17ddb4969973b9332a5324af42886e503c0f.zip | |
feat: add store purge command
This command will delete all records from the local store that cannot be
decrypted with the current key.
If a verify fails before running this, it should pass _after_ running
it.
Required afterwards:
- A `push --force`, to allow ensuring the remote store equals the local
store (deletions have now occured!)
- A `pull --force`, as once remote has been forced then local needs the
same
Nice to have:
- Provide "old" keys to purge, in case the are not lost. Or maybe rekey.
Diffstat (limited to 'atuin-client/src/record')
| -rw-r--r-- | atuin-client/src/record/sqlite_store.rs | 31 | ||||
| -rw-r--r-- | atuin-client/src/record/store.rs | 2 |
2 files changed, 33 insertions, 0 deletions
diff --git a/atuin-client/src/record/sqlite_store.rs b/atuin-client/src/record/sqlite_store.rs index 5df446b4..4e4a3756 100644 --- a/atuin-client/src/record/sqlite_store.rs +++ b/atuin-client/src/record/sqlite_store.rs @@ -145,6 +145,15 @@ impl Store for SqliteStore { Ok(res) } + async fn delete(&self, id: RecordId) -> Result<()> { + sqlx::query("delete from store where id = ?1") + .bind(id.0.as_hyphenated().to_string()) + .execute(&self.pool) + .await?; + + Ok(()) + } + async fn last(&self, host: HostId, tag: &str) -> Result<Option<Record<EncryptedData>>> { let res = sqlx::query("select * from store where host=?1 and tag=?2 order by idx desc limit 1") @@ -312,6 +321,28 @@ impl Store for SqliteStore { Ok(()) } + + /// Verify that every record in this store can be decrypted with the current key + /// Someday maybe also check each tag/record can be deserialized, but not for now. + async fn purge(&self, key: &[u8; 32]) -> Result<()> { + let all = self.load_all().await?; + + for record in all.iter() { + match record.clone().decrypt::<PASETO_V4>(key) { + Ok(_) => continue, + Err(_) => { + println!( + "Failed to decrypt {}, deleting", + record.id.0.as_hyphenated() + ); + + self.delete(record.id).await?; + } + } + } + + Ok(()) + } } #[cfg(test)] diff --git a/atuin-client/src/record/store.rs b/atuin-client/src/record/store.rs index 04fba630..1d812549 100644 --- a/atuin-client/src/record/store.rs +++ b/atuin-client/src/record/store.rs @@ -21,6 +21,7 @@ pub trait Store { ) -> Result<()>; async fn get(&self, id: RecordId) -> Result<Record<EncryptedData>>; + async fn delete(&self, id: RecordId) -> Result<()>; async fn len(&self, host: HostId, tag: &str) -> Result<u64>; async fn len_tag(&self, tag: &str) -> Result<u64>; @@ -30,6 +31,7 @@ pub trait Store { async fn re_encrypt(&self, old_key: &[u8; 32], new_key: &[u8; 32]) -> Result<()>; async fn verify(&self, key: &[u8; 32]) -> Result<()>; + async fn purge(&self, key: &[u8; 32]) -> Result<()>; /// Get the next `limit` records, after and including the given index async fn next( |
